ViewPager懶加載極致優(yōu)化

目錄介紹

  • 01.ViewPager簡單介紹
  • 02.ViewPager弊端分析
  • 03.ViewPager預(yù)加載
  • 04.ViewPager部分源碼
  • 05.懶加載出現(xiàn)問題
  • 06.如何實現(xiàn)預(yù)加載機制
  • 07.懶加載配合狀態(tài)管理器

呂詩禹想換個工作舒萎,渴望同行內(nèi)推

  • 個人信息
  • 感謝同行朋友懂鸵,如果可以,可以直接電話聯(lián)系或者微信聯(lián)系茶鉴!

好消息

  • 博客筆記大匯總【16年3月到至今】,包括Java基礎(chǔ)及深入知識點固该,Android技術(shù)博客头谜,Python學(xué)習(xí)筆記等等,還包括平時開發(fā)中遇到的bug匯總雾棺,當(dāng)然也在工作之余收集了大量的面試題,長期更新維護并且修正衬浑,持續(xù)完善……開源的文件是markdown格式的捌浩!同時也開源了生活博客,從12年起工秩,積累共計N篇[近100萬字尸饺,陸續(xù)搬到網(wǎng)上],轉(zhuǎn)載請注明出處助币,謝謝浪听!
  • 鏈接地址:https://github.com/yangchong211/YCBlogs
  • 如果覺得好,可以star一下眉菱,謝謝迹栓!當(dāng)然也歡迎提出建議,萬事起于忽微俭缓,量變引起質(zhì)變克伊!

01.ViewPager簡單介紹

  • ViewPager使用一個鍵對象來關(guān)聯(lián)每一頁酥郭,而不是管理View。這個鍵用于追蹤和唯一標(biāo)識在adapter中獨立位置中的一頁愿吹。調(diào)用方法startUpdate(ViewGroup)表明ViewPager中的內(nèi)容需要更改不从。
  • 通過調(diào)用一次或多次調(diào)用instantiateItem(ViewGroup, int)來構(gòu)造頁面視圖。
  • 調(diào)用destroyItem(ViewGroup, int, Object)來取消ViewPager關(guān)聯(lián)的頁面視圖犁跪。
  • 最后椿息,當(dāng)一次更新(添加和/或移除)完成之后將會調(diào)用finishUpdate(ViewGroup)來通知adapter, 提交關(guān)聯(lián)和/或取消關(guān)聯(lián)的操作。這三個方法就是用于ViewPager使用回調(diào)的方式來通知PagerAdapter來管理其中的頁面坷衍。
  • 一個非常簡單的方式就是使用每頁視圖作為key來關(guān)聯(lián)它們自己撵颊,在方法instantiateItem(ViewGroup, int)中創(chuàng)建和添加它們到ViewGroup之后,返回該頁視圖惫叛。與之相匹配的方法destroyItem(ViewGroup, int, Object)實現(xiàn)從ViewGroup中移除視圖。當(dāng)然必須在isViewFromObject(View, Object)中這樣實現(xiàn):return view == object;.
  • PagerAdapter支持數(shù)據(jù)改變時刷新界面逞刷,數(shù)據(jù)改變必須在主線程中調(diào)用嘉涌,并在數(shù)據(jù)改變完成后調(diào)用方法notifyDataSetChanged(), 和AdapterView中派生自BaseAdapter相似。一次數(shù)據(jù)的改變可能關(guān)聯(lián)著頁面的添加夸浅、移除仑最、或改變位置搓侄。ViewPager將根據(jù)adapter中實現(xiàn)getItemPosition(Object)方法返回的結(jié)果憋他,來判斷是否保留當(dāng)前已經(jīng)構(gòu)造的活動頁面(即重用粗俱,而不完全自行構(gòu)造)恬汁。

02.ViewPager弊端分析

  • 普通的viewpager如果你不使用setoffscreenpagelimit(int limit)這個方法去設(shè)置默認加載數(shù)的話是會默認加載頁面的左右兩頁的落追,也就是說當(dāng)你進入viewpager第一頁的時候第二頁和第一頁是會被一起加載的赋除,這樣同時加載就會造成一些問題撤蚊,試想我們?nèi)绻O(shè)置了setoffscreenpagelimit為3的話逮诲,那么進入viewpager以后就會同時加載4個fragment婉刀,像我們平時的項目中在這些fragment中一般都是會發(fā)送網(wǎng)絡(luò)請求的吟温,也就是說我們有4個fragment同時發(fā)送網(wǎng)絡(luò)請求去獲取數(shù)據(jù),這樣的結(jié)果顯而易見給用戶的體驗是不好的(如:浪費用戶流量突颊,造成卡頓等等)鲁豪。
  • 懶加載的實現(xiàn)弊端
    • 概念:當(dāng)需要時才加載,加載之后一直保持該對象律秃。
    • 而關(guān)于Fragment實現(xiàn)的PagerAdapter都沒有完全保存其引用和狀態(tài)爬橡。FragmentPageAdapter需要重建視圖,F(xiàn)ragmentStatePageAdapter使用狀態(tài)恢復(fù)棒动,View都被銷毀糙申,但是恢復(fù)的方式不同,而通常我們想得到的結(jié)果是船惨,F(xiàn)ragment一旦被加載郭宝,其視圖也不會被銷毀辞槐,即不會再重新走一遍生命周期。而且ViewPager為了實現(xiàn)滑動效果粘室,都是預(yù)加載左右兩側(cè)的頁面榄檬。
    • 我們通常想要實現(xiàn)的兩種效果:不提供滑動,需要時才構(gòu)造衔统,并且只走一遍生命周期鹿榜,避免在Fragment中做過多的狀態(tài)保存和恢復(fù)。

03.ViewPager預(yù)加載

  • ViewPager的預(yù)加載機制锦爵。那么舱殿,我們可不可以設(shè)置ViewPager的預(yù)加載為0,不就解決問題了嗎险掀?也就是代碼這樣操作:
    vp.setOffscreenPageLimit(0);
    
  • 然后看一下源碼
    • 即使你設(shè)置為0沪袭,那么還是會在里面判斷后設(shè)為默認值1。所以這個方法是行不通的樟氢。
    public void setOffscreenPageLimit(int limit) {
        if (limit < 1) {
            Log.w("ViewPager", "Requested offscreen page limit " + limit + " too small; defaulting to " + 1);
            limit = 1;
        }
    
        if (limit != this.mOffscreenPageLimit) {
            this.mOffscreenPageLimit = limit;
            this.populate();
        }
    
    }
    
  • ViewPager默認情況下的加載冈绊,當(dāng)切換到當(dāng)前頁面時,會默認預(yù)加載左右兩側(cè)的布局到ViewPager中埠啃,盡管兩側(cè)的View并不可見的死宣,我們稱這種情況叫預(yù)加載;由于ViewPager對offscreenPageLimit設(shè)置了限制碴开,頁面的預(yù)加載是不可避免……
  • 初始化緩存(mOffscreenPageLimit == 1)
    • 當(dāng)初始化時毅该,當(dāng)前顯示頁面是第0頁;mOffscreenPageLimit為1潦牛,所以預(yù)加載頁面為第1頁眶掌,再往后的頁面就不需要加載了(這里的2, 3巴碗, 4頁)
    • image
  • 中間頁面緩存(mOffscreenPageLimit == 1)
    • 當(dāng)向右滑動到第2頁時畏线,左右分別需要緩存一頁,第0頁就需要銷毀掉良价,第3頁需要預(yù)加載寝殴,第4頁不需要加載
    • image

04.ViewPager部分源碼

  • ViewPager.setAdapter方法
    • 銷毀舊的Adapter數(shù)據(jù),用新的Adaper更新UI
    • 清除舊的Adapter明垢,對已加載的item調(diào)用destroyItem蚣常,
    • 將自身滾動到初始位置this.scrollTo(0, 0)
    • 設(shè)置PagerObserver: mAdapter.setViewPagerObserver(mObserver);
    • 調(diào)用populate()方法計算并初始化View(這個方法后面會詳細介紹)
    • 如果設(shè)置了OnAdapterChangeListener,進行回調(diào)
  • ViewPager.populate(int newCurrentItem)
    • 該方法是ViewPager非常重要的方法痊银,主要根據(jù)參數(shù)newCurrentItem和mOffscreenPageLimit計算出需要初始化的頁面和需要銷毀頁面抵蚊,然后通過調(diào)用Adapter的instantiateItem和destroyItem兩個方法初始化新頁面和銷毀不需要的頁面!
    • 根據(jù)newCurrentItem和mOffscreenPageLimit計算要加載的page頁面,計算出startPos和endPos
    • 根據(jù)startPos和endPos初始化頁面ItemInfo贞绳,先從緩存里面獲取谷醉,如果沒有就調(diào)用addNewItem方法,實際調(diào)用mAdapter.instantiateItem
    • 將不需要的ItemInfo移除: mItems.remove(itemIndex),并調(diào)用mAdapter.destroyItem方法
    • 設(shè)置LayoutParams參數(shù)(包括position和widthFactor)冈闭,根據(jù)position排序待繪制的View列表:mDrawingOrderedChildren俱尼,重寫了getChildDrawingOrder方法
    • 最后一步獲取當(dāng)前顯示View的焦點:currView.requestFocus(View.FOCUS_FORWARD)
  • ViewPager.dataSetChanged()
    • 當(dāng)調(diào)用Adapter的notifyDataSetChanged時,會觸發(fā)這個方法萎攒,該方法會重新計算當(dāng)前頁面的position遇八,
    • 移除需要銷毀的頁面的ItemInfo對象,然后再調(diào)用populate方法刷新頁面
    • 循環(huán)mItems(每個page對應(yīng)的ItemInfo對象)耍休,調(diào)用int newPos = mAdapter.getItemPosition方法
    • 當(dāng)newPos等于PagerAdapter.POSITION_UNCHANGED表示當(dāng)前頁面不需要更新刃永,不用銷毀,當(dāng)newPos等于PagerAdapter.POSITION_NONE時羊精,需要更新斯够,移除item,調(diào)用mAdapter.destroyItem
    • 循環(huán)完成后喧锦,最后計算出顯示頁面的newCurrItem读规,調(diào)用setCurrentItemInternal(newCurrItem, false, true)方法更新UI(實際調(diào)用populate方法重新計算頁面信息)
  • ViewPager.scrollToItem(int item, boolean smoothScroll, int velocity, boolean dispatchSelected)
    • 滑動到指定頁面,內(nèi)部會觸發(fā)OnPageChangeListener
  • ViewPager.calculatePageOffsets(ItemInfo curItem, int curIndex, ItemInfo oldCurInfo)
    • 這個方法主要用于計算每個頁面對應(yīng)ItemInfo的offset變量裸违,這個變量用于記錄當(dāng)前view在所有緩存View中(包含當(dāng)前顯示頁)的索引,用于布局的時候計算該View應(yīng)該放在哪個位置
    • 在populate方法中更新完頁面數(shù)據(jù)后本昏,會調(diào)用該方法計算所有頁面的offset

05.懶加載出現(xiàn)問題

  • 發(fā)現(xiàn)Fragment中有一個setUserVisibleHint(boolean isVisibleToUser)方法供汛,這個方法就是告訴用戶,UI對用戶是否可見涌穆,可以做懶加載初始化操作怔昨。
    • 因為ViewPager會加載好多Fragment,為了節(jié)省內(nèi)容等會在Fragment不可見的某個時候調(diào)用onDestroyView()將用戶界面銷毀掉但是Fragment的實例還在宿稀,所以可能第一次加載沒有問題趁舀,但是再次回到第一個Fragment再去加載的時候就會出現(xiàn)UI對用戶可見但是視圖還沒有初始化。
  • 懶加載需要處理的幾個問題
    • 預(yù)加載祝沸,雖然沒有顯示在界面上矮烹,但是當(dāng)前頁面的上一頁和下一頁的Fragment已經(jīng)執(zhí)行了一個Fragment能夠顯示在界面上的所有生命周期方法,但是我們想在跳轉(zhuǎn)到該頁時才真正構(gòu)造數(shù)據(jù)視圖和請求數(shù)據(jù)罩锐。那么我們可以使用一個占位視圖奉狈,那么可以想到使用ViewStub,當(dāng)真正跳轉(zhuǎn)到該頁時涩惑,執(zhí)行ViewStub.inflate()方法仁期,加載真正的數(shù)據(jù)視圖和請求數(shù)據(jù)。
  • 視圖保存
    • 當(dāng)某一頁超出可視范圍和預(yù)加載范圍,那么它將會被銷毀跛蛋,F(xiàn)ragmentStatePagerAdapter銷毀整個Fragment, 我們可以自己保存該Fragment,或使用FragmentPagerAdapter讓FragmentTransition來保留Fragment的引用熬的。雖然這樣,但是它的周期方法已經(jīng)走完赊级,那么我們只能手動的保存Fragment根View的引用押框,當(dāng)再次重新進入新的聲明周期方法時,返回原來的View
  • 是否已經(jīng)被用戶所看到
    • 其實本身而言此衅,F(xiàn)ragmentManager并沒有提供為Fragment被用戶所看到的回調(diào)方法强戴,而是在FragmentPagerAdapter和FragmentStatePagerAdapter中,調(diào)用了Fragment.setUserVisibleHint(boolean)來表明Fragment是否已經(jīng)被作為primaryFragment. 所以這個方法可以被認為是一個回調(diào)方法挡鞍。

06.如何實現(xiàn)預(yù)加載機制

  • 主要的方法是Fragment中的setUserVisibleHint()骑歹,此方法會在onCreateView()之前執(zhí)行,當(dāng)viewPager中fragment改變可見狀態(tài)時也會調(diào)用,當(dāng)fragment 從可見到不見墨微,或者從不可見切換到可見道媚,都會調(diào)用此方法,使用getUserVisibleHint() 可以返回fragment是否可見狀態(tài)翘县。
  • 在BaseLazyFragment中需要在onActivityCreated()及setUserVisibleHint()方法中都調(diào)了一次lazyLoad() 方法最域。如果僅僅在setUserVisibleHint()調(diào)用lazyLoad(),當(dāng)默認首頁首先加載時會導(dǎo)致viewPager的首頁第一次展示時沒有數(shù)據(jù)顯示锈麸,切換一下才會有數(shù)據(jù)镀脂。因為首頁fragment的setUserVisible()在onActivityCreated() 之前調(diào)用,此時isPrepared為false 導(dǎo)致首頁fragment 沒能調(diào)用onLazyLoad()方法加載數(shù)據(jù)忘伞。
    /**
     * <pre>
     *     @author yangchong
     *     blog  : https://github.com/yangchong211
     *     time  : 2017/7/22
     *     desc  : 懶加載
     *     revise: 懶加載時機:onCreateView()方法執(zhí)行完畢 + setUserVisibleHint()方法返回true
     * </pre>
     */
    public abstract class BaseLazyFragment extends BaseFragment {
    
        /*
         * 預(yù)加載頁面回調(diào)的生命周期流程:
         * setUserVisibleHint() -->onAttach() --> onCreate()-->onCreateView()-->
         *              onActivityCreate() --> onStart() --> onResume()
         */
    
        /**
         * 懶加載過
         */
        protected boolean isLazyLoaded = false;
        /**
         * Fragment的View加載完畢的標(biāo)記
         */
        private boolean isPrepared = false;
    
        /**
         * 第一步,改變isPrepared標(biāo)記
         * 當(dāng)onViewCreated()方法執(zhí)行時,表明View已經(jīng)加載完畢,此時改變isPrepared標(biāo)記為true,并調(diào)用lazyLoad()方法
         */
        @Override
        public void onActivityCreated(@Nullable Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            isPrepared = true;
            //只有Fragment onCreateView好了
            //另外這里調(diào)用一次lazyLoad()
            lazyLoad();
        }
    
    
        /**
         * 第二步
         * 此方法會在onCreateView()之前執(zhí)行
         * 當(dāng)viewPager中fragment改變可見狀態(tài)時也會調(diào)用
         * 當(dāng)fragment 從可見到不見薄翅,或者從不可見切換到可見,都會調(diào)用此方法
         * true表示當(dāng)前頁面可見氓奈,false表示不可見
         */
        @Override
        public void setUserVisibleHint(boolean isVisibleToUser) {
            super.setUserVisibleHint(isVisibleToUser);
            LogUtil.d("setUserVisibleHint---"+isVisibleToUser);
            //只有當(dāng)fragment可見時翘魄,才進行加載數(shù)據(jù)
            if (isVisibleToUser){
                lazyLoad();
            }
        }
    
        /**
         * 調(diào)用懶加載
         * 第三步:在lazyLoad()方法中進行雙重標(biāo)記判斷,通過后即可進行數(shù)據(jù)加載
         */
        private void lazyLoad() {
            if (getUserVisibleHint() && isPrepared && !isLazyLoaded) {
                showFirstLoading();
                onLazyLoad();
                isLazyLoaded = true;
            } else {
                //當(dāng)視圖已經(jīng)對用戶不可見并且加載過數(shù)據(jù),如果需要在切換到其他頁面時停止加載數(shù)據(jù)舀奶,可以覆寫此方法
                if (isLazyLoaded) {
                    stopLoad();
                }
            }
        }
    
        /**
         * 視圖銷毀的時候講Fragment是否初始化的狀態(tài)變?yōu)閒alse
         */
        @Override
        public void onDestroyView() {
            super.onDestroyView();
            isLazyLoaded = false;
            isPrepared = false;
        }
    
        /**
         * 第一次可見時暑竟,操作該方法,可以用于showLoading操作育勺,注意這個是全局加載loading
         */
        protected void showFirstLoading() {
            LogUtil.i("第一次可見時show全局loading");
        }
    
        /**
         * 停止加載
         * 當(dāng)視圖已經(jīng)對用戶不可見并且加載過數(shù)據(jù)但荤,但是沒有加載完,而只是加載loading涧至。
         * 如果需要在切換到其他頁面時停止加載數(shù)據(jù)纱兑,可以覆寫此方法。
         * 存在問題化借,如何停止加載網(wǎng)絡(luò)
         */
        protected void stopLoad(){
    
        }
    
        /**
         * 第四步:定義抽象方法onLazyLoad(),具體加載數(shù)據(jù)的工作,交給子類去完成
         */
        @UiThread
        protected abstract void onLazyLoad();
    }
    
  • onLazyLoad()加載數(shù)據(jù)條件
    • getUserVisibleHint()會返回是否可見狀態(tài)潜慎,這是fragment實現(xiàn)懶加載的關(guān)鍵,只有fragment 可見才會調(diào)用onLazyLoad() 加載數(shù)據(jù)。
    • isPrepared參數(shù)在系統(tǒng)調(diào)用onActivityCreated時設(shè)置為true,這時onCreateView方法已調(diào)用完畢(一般我們在這方法里執(zhí)行findviewbyid等方法),確保 onLazyLoad()方法不會報空指針異常。
    • isLazyLoaded確保ViewPager來回切換時BaseFragment的initData方法不會被重復(fù)調(diào)用铐炫,onLazyLoad在該Fragment的整個生命周期只調(diào)用一次,第一次調(diào)用onLazyLoad()方法后馬上執(zhí)行 isLazyLoaded = true垒手。
    • 然后再繼承這個BaseLazyFragment實現(xiàn)onLazyLoad() 方法就行。他會自動控制當(dāng)fragment 展現(xiàn)出來時倒信,才會加載數(shù)據(jù)
  • 還有幾個細節(jié)需要優(yōu)化一下
    • 當(dāng)視圖已經(jīng)對用戶不可見并且加載過數(shù)據(jù)科贬,如果需要在切換到其他頁面時停止加載數(shù)據(jù),可以覆寫此方法鳖悠,也就是stopLoad
    • 視圖銷毀的時候講Fragment是否初始化的狀態(tài)變?yōu)閒alse榜掌,這個也需要處理一下
    • 第一次可見時,定義一個showFirstLoading方法乘综,操作該方法憎账,可以用于Loading加載操作,注意這個是全局加載loading卡辰,和下拉刷新數(shù)據(jù)或者局部刷新的loading不一樣的胞皱。可能有些開發(fā)app九妈,沒有將loading分的這么細反砌。

07.懶加載配合狀態(tài)管理器

  • 什么是狀態(tài)管理器?
    • 一般在需要用戶等待的場景萌朱,顯示一個Loading動畫可以讓用戶知道App正在加載數(shù)據(jù)宴树,而不是程序卡死,從而給用戶較好的使用體驗晶疼。
    • 當(dāng)加載的數(shù)據(jù)為空時顯示一個數(shù)據(jù)為空的視圖酒贬、在數(shù)據(jù)加載失敗時顯示加載失敗對應(yīng)的UI并支持點擊重試會比白屏的用戶體驗更好一些。
    • 加載中冒晰、加載失敗同衣、空數(shù)據(jù)的UI風(fēng)格竟块,一般來說在App內(nèi)的所有頁面中需要保持一致壶运,也就是需要做到全局統(tǒng)一。
  • 如何降低偶性和入侵性
    • 讓View狀態(tài)的切換和Activity徹底分離開浪秘,必須把這些狀態(tài)View都封裝到一個管理類中蒋情,然后暴露出幾個方法來實現(xiàn)View之間的切換。
      在不同的項目中可以需要的View也不一樣耸携,所以考慮把管理類設(shè)計成builder模式來自由的添加需要的狀態(tài)View棵癣。
    • 那么如何降低耦合性,讓代碼入侵性低夺衍。方便維護和修改狈谊,且移植性強呢?大概具備這樣的條件……
      • 可以運用在activity或者fragment中
      • 不需要在布局中添加LoadingView,而是統(tǒng)一管理不同狀態(tài)視圖河劝,同時暴露對外設(shè)置自定義狀態(tài)視圖方法壁榕,方便UI特定頁面定制
      • 支持設(shè)置自定義不同狀態(tài)視圖,即使在BaseActivity統(tǒng)一處理狀態(tài)視圖管理赎瞎,也支持單個頁面定制
      • 在加載視圖的時候像異常和空頁面能否用ViewStub代替牌里,這樣減少繪制,只有等到出現(xiàn)異常和空頁面時务甥,才將視圖給inflate出來
      • 當(dāng)頁面出現(xiàn)網(wǎng)絡(luò)異常頁面牡辽,空頁面等,頁面會有交互事件敞临,這時候可以設(shè)置點擊設(shè)置網(wǎng)絡(luò)或者點擊重新加載等等
  • 那么具體怎么操作呢态辛?
    • 可以自由切換內(nèi)容,空數(shù)據(jù)哟绊,異常錯誤因妙,加載,網(wǎng)絡(luò)錯誤等5種狀態(tài)票髓。父類BaseFragment直接暴露5中狀態(tài)攀涵,方便子類統(tǒng)一管理狀態(tài)切換,這里fragment的封裝和activity差不多洽沟。
    /**
     * <pre>
     *     @author yangchong
     *     blog  : https://github.com/yangchong211
     *     time  : 2017/7/20
     *     desc  : fragment的父類
     *     revise: 注意以故,該類具有懶加載
     * </pre>
     */
    public abstract class BaseStateFragment extends BaseLazyFragment {
    
        protected StateLayoutManager statusLayoutManager;
        private View view;
    
        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                                 @Nullable Bundle savedInstanceState) {
            if(view==null){
                view = inflater.inflate(R.layout.base_state_view, container , false);
                initStatusLayout();
                initBaseView(view);
            }
            return view;
        }
    
        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            initView(view);
            initListener();
        }
    
        @Override
        public void onActivityCreated(@Nullable Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
        }
    
        /**
         * 獲取到子布局
         * @param view              view
         */
        private void initBaseView(View view) {
            LinearLayout llStateView = view.findViewById(R.id.ll_state_view);
            llStateView.addView(statusLayoutManager.getRootLayout());
        }
    
    
        /**
         * 初始化狀態(tài)管理器相關(guān)操作
         */
        protected abstract void initStatusLayout();
    
        /**
         * 初始化View的代碼寫在這個方法中
         * @param view              view
         */
        public abstract void initView(View view);
    
        /**
         * 初始化監(jiān)聽器的代碼寫在這個方法中
         */
        public abstract void initListener();
    
        /**
         * 第一次可見狀態(tài)時,showLoading操作裆操,注意下拉刷新操作時不要用該全局loading
         */
        @Override
        protected void showFirstLoading() {
            super.showFirstLoading();
            showLoading();
        }
    
        /*protected void initStatusLayout() {
            statusLayoutManager = StateLayoutManager.newBuilder(activity)
                    .contentView(R.layout.common_fragment_list)
                    .emptyDataView(R.layout.view_custom_empty_data)
                    .errorView(R.layout.view_custom_data_error)
                    .loadingView(R.layout.view_custom_loading_data)
                    .netWorkErrorView(R.layout.view_custom_network_error)
                    .build();
        }*/
    
    
        /*---------------------------------下面是狀態(tài)切換方法-----------------------------------------*/
    
    
        /**
         * 加載成功
         */
        protected void showContent() {
            if (statusLayoutManager!=null){
                statusLayoutManager.showContent();
            }
        }
    
        /**
         * 加載無數(shù)據(jù)
         */
        protected void showEmptyData() {
            if (statusLayoutManager!=null){
                statusLayoutManager.showEmptyData();
            }
        }
    
        /**
         * 加載異常
         */
        protected void showError() {
            if (statusLayoutManager!=null){
                statusLayoutManager.showError();
            }
        }
    
        /**
         * 加載網(wǎng)絡(luò)異常
         */
        protected void showNetWorkError() {
            if (statusLayoutManager!=null){
                statusLayoutManager.showNetWorkError();
            }
        }
    
        /**
         * 加載loading
         */
        protected void showLoading() {
            if (statusLayoutManager!=null){
                statusLayoutManager.showLoading();
            }
        }
    }
    
    //如何切換狀態(tài)呢怒详?
    showContent();
    showEmptyData();
    showError();
    showLoading();
    showNetWorkError();
    
    //或者這樣操作也可以
    statusLayoutManager.showLoading();
    statusLayoutManager.showContent();
    
  • 狀態(tài)管理器的設(shè)計思路
    • StateFrameLayout是繼承FrameLayout自定義布局,主要是存放不同的視圖踪区,以及隱藏和展示視圖操作
    • StateLayoutManager是狀態(tài)管理器昆烁,主要是讓開發(fā)者設(shè)置不同狀態(tài)視圖的view,以及切換視圖狀態(tài)操作
      • 幾種異常狀態(tài)要用ViewStub缎岗,因為在界面狀態(tài)切換中l(wèi)oading和內(nèi)容View都是一直需要加載顯示的静尼,但是其他的3個只有在沒數(shù)據(jù)或者網(wǎng)絡(luò)異常的情況下才會加載顯示,所以用ViewStub來加載他們可以提高性能传泊。
    • OnRetryListener鼠渺,為接口,主要是重試作用眷细。比如加載失敗了拦盹,點擊視圖需要重新刷新接口,則可以用到這個溪椎。開發(fā)者也可以自己設(shè)置點擊事件
    • 關(guān)于狀態(tài)視圖切換方案普舆,目前市場有多種做法恬口,具體可以看我的這篇博客:https://juejin.im/post/5d2f014d6fb9a07ea648a959

其他介紹

01.關(guān)于博客匯總鏈接

02.關(guān)于我的博客

項目地址:https://github.com/yangchong211/YCStateLayout

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市沼侣,隨后出現(xiàn)的幾起案子楷兽,更是在濱河造成了極大的恐慌,老刑警劉巖华临,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芯杀,死亡現(xiàn)場離奇詭異,居然都是意外死亡雅潭,警方通過查閱死者的電腦和手機揭厚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來扶供,“玉大人筛圆,你說我怎么就攤上這事〈慌ǎ” “怎么了太援?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長扳碍。 經(jīng)常有香客問我提岔,道長,這世上最難降的妖魔是什么笋敞? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任碱蒙,我火速辦了婚禮,結(jié)果婚禮上夯巷,老公的妹妹穿的比我還像新娘赛惩。我一直安慰自己,他們只是感情好趁餐,可當(dāng)我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布喷兼。 她就那樣靜靜地躺著,像睡著了一般后雷。 火紅的嫁衣襯著肌膚如雪季惯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天喷面,我揣著相機與錄音星瘾,去河邊找鬼走孽。 笑死惧辈,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的磕瓷。 我是一名探鬼主播盒齿,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼念逞,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了边翁?” 一聲冷哼從身側(cè)響起翎承,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎符匾,沒想到半個月后叨咖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡啊胶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年甸各,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片焰坪。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡趣倾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出某饰,到底是詐尸還是另有隱情儒恋,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布黔漂,位于F島的核電站诫尽,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏炬守。R本人自食惡果不足惜箱锐,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望劳较。 院中可真熱鬧驹止,春花似錦、人聲如沸观蜗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽墓捻。三九已至抖仅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間砖第,已是汗流浹背撤卢。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留梧兼,地道東北人放吩。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像羽杰,于是被迫代替她去往敵國和親渡紫。 傳聞我的和親對象是個殘疾皇子到推,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,527評論 2 349

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