9行代碼讓你App內(nèi)的Fragment對(duì)重疊說再見

在上一篇從源碼角度分析,為什么會(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è)更好的解決問題的思路!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容