在上一篇從源碼角度分析,為什么會(huì)發(fā)生Fragment重疊?里,我們分析了造成Fragment重疊的原因,這一篇我會(huì)介紹幾個(gè)解決方案,同時(shí)給出一個(gè)我的方案:9行代碼讓你app內(nèi)的Fragment對(duì)重疊說再見!
更新:
官方在Support 24.0.0及以上版本已經(jīng)修復(fù)了上篇源碼分析的引起重疊的BUG; 所以如果你使用了24.0.0以上的版本,在正常情況下,就不用再考慮重疊的問題了!
(PS: 依然推薦閱讀文末的 “補(bǔ)充” 部分)
通過findFragmentByTag() & getFragments()的解決方案
這兩種方案在我這篇簡書Fragment全解析系列(一):那些年踩過的坑里有比較詳細(xì)的介紹,可以自行查看,這里不多做介紹,這兩種方案也是最常見的解決方案。
缺點(diǎn):
- 使用比較麻煩,代碼量較多;
- 在Fragment嵌套的場景下,恢復(fù)會(huì)有問題,原因在于:頁面重啟后,在父Fragment沒有初始化完成前,
getChildFragmentManager()
子棧內(nèi)的子Fragment是空,只有父Fragment初始化完成后,子棧內(nèi)的子Fragment才能正確獲取到。
從源碼角度想到的解決方案:自己保存Fragment的Hidden狀態(tài)
下面這個(gè)方案,是我在完善Fragmentation庫時(shí)想到的方案。
Fragmentation庫前幾天push了改變重大的0.7版本,應(yīng)該算是比較成熟了,如果你重度使用Fragment或者想使用單Activity+多Fragment的組件架構(gòu)的話,強(qiáng)烈推薦看看;對(duì)于各種復(fù)雜嵌套、同級(jí)Fragment的使用場景,你不必?fù)?dān)心Fragment的重疊,同時(shí)大大簡化Fragment的嵌套邏輯。
在上一篇簡書分析中,我們知道了發(fā)生Fragment重疊的根本原因在于FragmentState沒有保存Fragment的顯示狀態(tài),即mHidden
,導(dǎo)致頁面重啟后,該值為默認(rèn)的false,即show狀態(tài),所以導(dǎo)致了Fragment的重疊。
根據(jù)這個(gè)原因,我想到我們手動(dòng)維護(hù)一個(gè)mSupportHidden
不就行了嗎?
看下面的基類Fragment代碼:
public class BaseFragment extends Fragment {
private static final String STATE_SAVE_IS_HIDDEN = "STATE_SAVE_IS_HIDDEN";
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
...
if (savedInstanceState != null) {
boolean isSupportHidden = savedInstanceState.getBoolean(STATE_SAVE_IS_HIDDEN);
FragmentTransaction ft = getFragmentManager().beginTransaction();
if (isSupportHidden) {
ft.hide(this);
} else {
ft.show(this);
}
ft.commit();
}
@Override
public void onSaveInstanceState(Bundle outState) {
...
outState.putBoolean(STATE_SAVE_IS_HIDDEN, isHidden());
}
}
是的你沒看錯(cuò),只要上面的9行代碼! FragmentState沒幫我們保存Hidden狀態(tài),那就我們自己來保存,在頁面重啟后,我們自己來決定Fragment是否顯示!
解決思路轉(zhuǎn)變了,由Activity/父Fragment來管理子Fragment的Hidden狀態(tài)轉(zhuǎn)變?yōu)?由Fragment自己來管理自己的Hidden狀態(tài)!
優(yōu)點(diǎn):不管多深的嵌套Fragment、同級(jí)Fragment等場景,全都可以正常工作,不會(huì)發(fā)生重疊!
缺點(diǎn):無。
補(bǔ)充
有些小伙伴反應(yīng)還是會(huì)重疊,其實(shí)是因?yàn)榧虞d根Fragment時(shí)沒有經(jīng)過判斷的原因,當(dāng)在類似onCreate
等里加載根Fragment時(shí),需要下面的判斷,避免重復(fù)加載相同的Fragment:
(PS:add
,replace
情況下,如果沒有加入回退棧,則不判斷也不會(huì)造成重疊;若加入回退棧,則也會(huì)造成重疊現(xiàn)象,建議統(tǒng)一判斷下)
public class MainActivity ... {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
...
// 這里一定要在save為null時(shí)才加載Fragment,F(xiàn)ragment中onCreateView等生命周里加載根子Fragment同理
// 因?yàn)樵陧撁嬷貑r(shí),F(xiàn)ragment會(huì)被保存恢復(fù),而此時(shí)再加載Fragment會(huì)重復(fù)加載,導(dǎo)致重疊
if(findFragmentByTag(rootFragmentTag) == null){
// 正常情況下去 加載根Fragment
}
}
}
最后
最后的方案用在了我的Fragmentation庫中,在新的仿知乎的Demo里,各種復(fù)雜場景表現(xiàn)完美。
但是這個(gè)方案真的神奇的不可思議,在我的測試下,各種情況都正常適用,但是之前從沒看到有人提到過該方案,所以如果你發(fā)現(xiàn)該方案有我沒有考慮到的BUG,請(qǐng)第一時(shí)間告訴我!
官方在Support 24.0.0及以上版本已經(jīng)從底層使用該方式修復(fù)該BUG;如果你還在使用 < 24.0.0 的版本,還是需要這 ”9行代碼“的~
最后是一點(diǎn)心得體會(huì):
當(dāng)遇到問題時(shí),我們?nèi)绻麖脑搭^思考:為什么會(huì)發(fā)生這個(gè)問題? 從源碼角度分析問題,可能就會(huì)得到一個(gè)更好的解決問題的思路!