PagerAdapter深度解析和實踐優化

目錄介紹

  • 01.PagerAdapter簡單介紹
  • 02.PagerAdapter抽象方法
  • 03.PagerAdapter原理介紹
  • 04.PagerAdapter緩存和銷燬
  • 05.自定義PagerAdapter
  • 06.PagerAdapter兩個子類
  • 07.三種Adapter的總結

ViewPager相關

01.PagerAdapter簡單介紹

  • 使用場景
    • 輪播圖:ViewPager+自定義PagerAdapter
    • fragment:TabLayout+ViewPager+FragmentPagerAdapter+Fragment

02.PagerAdapter抽象方法

  • 子類繼承PagerAdapter須要實現方法說明
    • Object instantiateItem(ViewGroup container, int position)
      • 一句話:要顯示的頁面或須要緩存的頁面,會調用這個方法進行佈局的初始化。
      • 這個方法是ViewPager須要加載某個頁面時調用,container就是ViewPager本身,position頁面索引;
      • 咱們須要實現的是添加一個view到container中,而後返回一個跟這個view可以關聯起來的對象,這個對象能夠是view自身,也能夠是其餘對象(好比FragmentPagerAdapter返回的就是一個Fragment),關鍵是在isViewFromObject可以將view和這個object關聯起來
    • void destroyItem(ViewGroup container, int position, Object object)
      • 一句話:當ViewPager須要銷燬一個頁面時調用,咱們須要將position對應的view從container中移除。
      • 這時參數除了position就只有object,其實就是上面instantiateItem方法返回的對象,這時要經過object找到對應的View,而後將其移除掉,若是你的instantiateItem方法返回的就是View,這裏就直接強轉成View移除便可:container.removeView((View) object);若是不是,通常會本身建立一個List緩存view列表,而後根據position從List中找到對應的view移除;(固然你也能夠不移除,內存泄漏)。
      • FragmentPagerAdapter的實現是:mCurTransaction.detach((Fragment)object),其實也就是將fragemnt的view從container中移除
    • isViewFromObject(View view, Object object)
      • 一句話:這個方法用於判斷是否由對象生成界面,官方建議直接返回 return view == object;。
      • 從名稱理解起來像是判斷view是否來自object,跟進一步解釋應該是上面instantiateItem方法中
      • 向container中添加的view和方法返回的對象二者之間一對一的關係;由於在ViewPager內部有個方法叫infoForChild,
      • 這個方法是經過view去找到對應頁面信息緩存類ItemInfo(內部調用了isViewFromObject),若是找不到,說明這個view是個野孩子,ViewPager會認爲不是Adapter提供的View,因此這個View不會顯示出來;
      • 總結一下:isViewFromObject 方法是讓view和object(內部爲ItemInfo)一一對應起來
    • int getItemPosition(Object object)
      • 改方法是判斷當前object對應的View是否須要更新,在調用notifyDataSetChanged時會間接觸發該方法,
      • 若是返回POSITION_UNCHANGED表示該頁面不須要更新,若是返回POSITION_NONE則表示該頁面無效了,須要銷燬並觸發destroyItem方法(而且有可能調用instantiateItem從新初始化這個頁面)

02.PagerAdapter原理介紹

  • ViewPager+PagerAdapter的合做關係:
    • ViewPager來控制一頁界面構造和銷燬的時機,使用回調來通知PagerAdapter具體作什麼,PagerAdapter只須要按照相應的步驟作。固然爲了使用得更好、提供更多的功能,又建議了使用View的回收工做和管理工做,同時提供當數據改變時的界面刷新工做。
  • instantiateItem(ViewGroup, int):
    • 構造指定位置的頁面。adapter負責在這個方法中添加view到容器中,即便是在finishUpdate(ViewGroup)才保證完成的。在FragmentPagerAdapter和FragmentStatePagerAdapter中,都是返回一個構造的Fragment.
  • destroyItem(ViewGroup, populate, Object):
    • 移除指定位置的頁面。adapter負責從容器中移除view,便是最後實在finishUpdate(ViewGroup)保證完成的。在FragmentPagerAdapter和FragmentStatePagerAdapter中,分別使用FragmentTransition.detach(Fragment)和FragmentTransition.remove(Fragment)來邏輯上銷燬Fragment.
  • finishUpdate(ViewGroup):
    • 當頁面的顯示變化完成式調用。在這裏,你必定保證全部的頁面從容器中合理的添加或移除掉。
  • setPrimaryItem(ViewGroup, int, Object):
    • 被ViewPager調用來通知adapter此時那個item應該被認爲是主要的頁面,這個頁面將在當前頁面展現給用戶。正是由於這個方法,纔有在ViewPager中實現Fragment懶加載的機制。
  • isViewFromObject(View, Object):
    • 指定當前頁面View是否和指定的key對象相關聯(這個key對象是在instantiateItem(ViewGroup, int)方法返回的)。這個方法須要PagerAdapter恰當的實現。即只要匹配好鍵值對便可。FragmentPagerAdapter和FragmentStatePagerAdapter的實現: return ((Fragment)object).getView() == view;.
  • 雖然簡單或不多使用到的一些方法不想細究,不過仍是一次性分析完爲好,如getPageTitle(int), getPageWidth(int), getItemPosition(Object)等。
    • getPageTitle(int): 返回每頁的標題,多用於關聯indicator
    • getPageWidth(int): 返回指定的頁面相對於ViewPager寬度的比例,範圍(0.f-1.f]。默認值爲1.f, 即佔滿整個屏幕。若是是0.5f, 那麼在初始狀態下,默認會出現前兩個頁面,而primary主頁面是在ViewPager的起始位置(一般是屏幕左側),直到最後一個頁面在屏幕右側,若是總共5個頁面,返回值爲0.2f, 那麼將一次性出現全部的頁面.
  • getItemPosition(Object):
    • 用於數據刷新時的頁面處理方式。返回值包括三類:POSITION_UNCHANGED表示位置沒有變化,即在添加或移除一頁或多頁以後該位置的頁面保持不變,能夠用於一個ViewPager中最後幾頁的添加或移除時,保持前幾頁仍然不變;POSITION_NONE,表示當前頁再也不做爲ViewPager的一頁數據,將被銷燬,能夠用於無視View緩存的刷新;根據傳過來的參數Object來判斷這個key所指定的新的位置

04.PagerAdapter緩存和銷燬

  • 在ViewPager三種Adapter的子view建立和銷燬的方法添加相關的日誌代碼,以下:
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        Log.d("yc", "destroyItem:" + position);
        //...省略部分代碼
    }
    
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Log.d("yc", "instantiateItem:" + position);
        //...省略部分代碼
    }
    複製代碼
  • 滑動ViewPager翻頁,觀察控制檯的輸出,三種Adapter針對不一樣界面、不一樣滑動方向的翻頁狀況打印以下:
    • image
    • 從圖中咱們能夠看到,三種Adapter在相同的狀況下,ViewPager的子頁面銷燬和建立時機是同樣。一般所聽到的都是FragmentPagerAdapter會緩存全部的Fragment子項,而上圖中咱們看到的是在滑動的過程當中它的destroyItem方法被調用了,而在滑動回來時相對應的子項Fragment也確實調用instantiateItem方法。這樣看來根本就沒有緩存……
  • 可是仔細對比了一下三個Adapter建立視圖的過程,發現上面推論有所欠缺。
    • 由於在使用Fragment做爲子視圖時,咱們是經過getItem方法返回Fragment的,單純從這裏打印instantiateItem的調用不表明Fragment真的徹底被從新建立了(從新建立表明須要從新add,即從頭走一遍生命週期,可是在這裏不能證實),也能夠經過兩個FragmentAdapter中instantiateItem的實現證實(觀察getItem方法的調用條件),因此又在Fragment對應的兩種Adapter的getItem中添加相應的log代碼,以下:
    @Override
    public Fragment getItem(int position) {
        Log.d("ccc", "getItem:" + position);
        return fragmentList.get(position);
    }
    複製代碼
  • 針對不一樣狀況,控制檯輸出結果以下:
    • image
    • 經過上圖咱們能夠看到,FragmentPagerAdapter在最後向右邊劃回來時並無調用getItem方法(getItem是建立一個新的Fragment),這也就說明了他沒有從新建立Fragment,證實了它會緩存全部Fragment,那麼它到底在哪裏作了緩存呢?具體看FragmentPagerAdapter分析……

05.自定義PagerAdapter

  • 好比,引導頁使用ViewPager,這個時候動態管理的Adapter,能夠每次都會建立新view,銷燬舊View。節省內存消耗性能。能夠說下面這種用的最多……
    /**
     * <pre>
     *     @author yangchong
     *     blog  : https://github.com/yangchong211
     *     time  : 2016/3/18
     *     desc  : 動態管理的Adapter。概念參照{@link android.support.v4.app.FragmentPagerAdapter}
     *             每次都會建立新view,銷燬舊View。節省內存消耗性能
     *     revise: 好比使用場景是啓動引導頁
     * </pre>
     */
    public abstract class AbsDynamicPagerAdapter extends PagerAdapter {
    
    	@Override
    	public boolean isViewFromObject(@NonNull View arg0, @NonNull Object arg1) {
    		return arg0==arg1;
    	}
    
    	@Override
    	public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
    		container.removeView((View) object);
    	}
    	
    	@Override
    	public int getItemPosition(@NonNull Object object) {
    		return super.getItemPosition(object);
    	}
    
    	@NonNull
    	@Override
    	public Object instantiateItem(@NonNull ViewGroup container, int position) {
    		View itemView = getView(container,position);
    		container.addView(itemView);
    		return itemView;
    	}
    
    	/**
    	 * 建立view
    	 * @param container					container
    	 * @param position					索引
    	 * @return
    	 */
    	public abstract View getView(ViewGroup container, int position);
    
    }
    複製代碼
  • 好比,常見有無限輪播圖,能夠自動輪播,你們應該用的特別多。這個時候能夠優化自定義輪播圖的PagerAdapter,建立集合用來存儲view,再次用的時候先取集合,沒有就建立。而不是頻繁建立視圖。
    /**
     * <pre>
     *     @author yangchong
     *     blog  : https://github.com/yangchong211
     *     time  : 2016/3/18
     *     desc  : AbsLoopPagerAdapter
     *     revise: 若是是自動輪播圖的話就用這一個
     * </pre>
     */
    public abstract class AbsLoopPagerAdapter extends PagerAdapter {
    
    
        private BannerView mViewPager;
        /**
         * 用來存放View的集合
         */
        private ArrayList<View> mViewList = new ArrayList<>();
        /**
         * 刷新所有
         */
        @Override
        public void notifyDataSetChanged() {
            mViewList.clear();
            initPosition();
            super.notifyDataSetChanged();
        }
    
        /**
         * 獲取item索引
         *
         * POSITION_UNCHANGED表示位置沒有變化,即在添加或移除一頁或多頁以後該位置的頁面保持不變,
         * 能夠用於一個ViewPager中最後幾頁的添加或移除時,保持前幾頁仍然不變;
         *
         * POSITION_NONE,表示當前頁再也不做爲ViewPager的一頁數據,將被銷燬,能夠用於無視View緩存的刷新;
         * 根據傳過來的參數Object來判斷這個key所指定的新的位置
         * @param object                        objcet
         * @return
         */
        @Override
        public int getItemPosition(@NonNull Object object) {
            return POSITION_NONE;
        }
    
        /**
         * 註冊數據觀察者監聽
         * @param observer                      observer
         */
        @Override
        public void registerDataSetObserver(@NonNull DataSetObserver observer) {
            super.registerDataSetObserver(observer);
            initPosition();
        }
    
        private void initPosition(){
            if (getRealCount()>1){
                if (mViewPager.getViewPager().getCurrentItem() == 0&&getRealCount()>0){
                    int half = Integer.MAX_VALUE/2;
                    int start = half - half%getRealCount();
                    setCurrent(start);
                }
            }
        }
    
        /**
         * 設置位置,利用反射實現
         * @param index                         索引
         */
        @TargetApi(Build.VERSION_CODES.KITKAT)
        private void setCurrent(int index){
            try {
                Field field = ViewPager.class.getDeclaredField("mCurItem");
                field.setAccessible(true);
                field.set(mViewPager.getViewPager(),index);
            } catch (NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    
        public AbsLoopPagerAdapter(BannerView viewPager){
            this.mViewPager = viewPager;
        }
    
        @Override
        public boolean isViewFromObject(@NonNull View arg0, @NonNull Object arg1) {
            return arg0==arg1;
        }
    
        /**
         * 若是頁面不是當前顯示的頁面也不是要緩存的頁面,會調用這個方法,將頁面銷燬。
         * @param container                     container
         * @param position                      索引
         * @param object                        object
         */
        @Override
        public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
            container.removeView((View) object);
            Log.d("PagerAdapter","銷燬的方法");
        }
    
        /**
         *  要顯示的頁面或須要緩存的頁面,會調用這個方法進行佈局的初始化。
         * @param container                     container
         * @param position                      索引
         * @return
         */
        @NonNull
        @Override
        public Object instantiateItem(@NonNull ViewGroup container, int position) {
            int realPosition = position%getRealCount();
            View itemView = findViewByPosition(container,realPosition);
            container.addView(itemView);
            Log.d("PagerAdapter","建立的方法");
            return itemView;
        }
    
        /**
         * 這個是避免重複建立,若是集合中有,則取集合中的
         * @param container                     container
         * @param position                      索引
         * @return
         */
        private View findViewByPosition(ViewGroup container, int position){
            for (View view : mViewList) {
                if (((int)view.getTag()) == position&&view.getParent()==null){
                    return view;
                }
            }
            View view = getView(container,position);
            view.setTag(position);
            mViewList.add(view);
            return view;
        }
    
    
        @Deprecated
        @Override
        public final int getCount() {
            //設置最大輪播圖數量 ,若是是1那麼就是1,不輪播;若是大於1則設置一個最大值,能夠輪播
            //return getRealCount();
            return getRealCount()<=1?getRealCount(): Integer.MAX_VALUE;
        }
    
        /**
         * 獲取輪播圖數量
         * @return                          數量
         */
        public abstract int getRealCount();
    
        /**
         * 建立view
         * @param container                 viewGroup
         * @param position                  索引
         * @return
         */
        public abstract View getView(ViewGroup container, int position);
    
    }
    複製代碼
  • 還有一種場景,靜態輪播圖,也就是不會自動輪播,可是手指能夠滑動,而且滑動到第一張不能往左滑動,滑動到最後一張不能向右滑動。這種場景,view添加進去就無論了,View就常在呢!
    /**
     * <pre>
     *     @author yangchong
     *     blog  : https://github.com/yangchong211
     *     time  : 2016/3/18
     *     desc  : 靜態存儲的Adapter,概念參照{@link android.support.v4.app.FragmentStatePagerAdapter}
     *             view添加進去就無論了,View長在,內存再也不
     *     revise: 若是是靜態輪播圖就用這個
     * </pre>
     */
    public abstract class AbsStaticPagerAdapter extends PagerAdapter {
    
        private ArrayList<View> mViewList = new ArrayList<>();
    
    	@Override
    	public boolean isViewFromObject(@NonNull View arg0, @NonNull Object arg1) {
    		return arg0==arg1;
    	}
    
    	@Override
    	public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
            container.removeView((View) object);
            Log.d("PagerAdapter","銷燬的方法");
        }
    
    	@Override
    	public void notifyDataSetChanged() {
            mViewList.clear();
            super.notifyDataSetChanged();
    	}
    
    	@Override
    	public int getItemPosition(@NonNull Object object) {
    		return POSITION_NONE;
    	}
    
    	@NonNull
        @Override
    	public Object instantiateItem(@NonNull ViewGroup container, int position) {
            View itemView = findViewByPosition(container,position);
            container.addView(itemView);
            onBind(itemView,position);
            Log.d("PagerAdapter","建立的方法");
    		return itemView;
    	}
    
        private View findViewByPosition(ViewGroup container, int position){
            for (View view : mViewList) {
                if (((int)view.getTag()) == position&&view.getParent()==null){
                    return view;
                }
            }
            View view = getView(container,position);
            view.setTag(position);
            mViewList.add(view);
            return view;
        }
    
    
        public void onBind(View view, int position){}
    
    	public abstract View getView(ViewGroup container, int position);
    
    }
    複製代碼
  • 這三種不一樣的使用場景,咱們應該都見到過,那麼自定義adpater的時候可否再優化一下,ok,上面的方案恰好合適。若是有不一樣的想法,歡迎提出……該源代碼的開源地址:github.com/yangchong21…

06.PagerAdapter兩個子類

  • PagerAdapter 的兩個直接子類 FragmentPagerAdapter 和 FragmentStatePagerAdapter 。而咱們經常會在 ViewPager 和 Fragment 結合使用的時候來使用這兩個適配器。

6.1 FragmentPagerAdapter

  • FragmentPagerAdapter 它將每個頁面表示爲一個 Fragment,而且每個 Fragment 都將會保存到 FragmentManager 當中。並且,當用戶沒可能再次回到頁面的時候,FragmentManager 纔會將這個 Fragment 銷燬。
    • FragmentPagerAdapter:對於再也不須要的 fragment,選擇調用 onDetach() 方法,僅銷燬視圖,並不會銷燬 fragment 實例。
  • 使用 FragmentPagerAdapter 須要實現兩個方法:
    • public Fragment getItem(int position) 返回的是對應的 Fragment 實例,通常咱們在使用時,會經過構造傳入一個要顯示的 Fragment 的集合,咱們只要在這裏把對應的 Fragment 返回就好了。
    • public int getCount() 這個上面介紹過了返回的是頁面的個數,咱們只要返回傳入集合的長度就好了。
    • 使用起來是很是簡單的,FragmentStatePagerAdapter 的使用也和上面同樣,那二者到底有什麼區別呢?
  • 錯誤說法
    • 超出範圍的Fragment會被銷燬。因此以前,我一直認爲的是,FragmentPagerAdapter中一般最多會保留3個Fragment, 超出左右兩側的Fragment將被銷燬,滑動到時又會被從新構造。
  • PagerAdapter的實現類,使用將一直保留在FragmentManager中的Fragment來表明每一頁,直到用戶返回上一頁。
    • 當用於典型地使用多靜態化的Fragment時,FragmentPagerAdapter無疑是最好使用的,例如一組tabs. 每一個用戶訪問過的頁面的Fragment都將會保留在內存中,即便它的視圖層在不可見時已經被銷燬。這可能致使使用比較大數量的內存,由於Fragment實例持有任意數量的狀態。若是使用大數據的頁面,考慮使用FragmentStatePagerAdapter.
    • 從上面能夠看出,即便是超出可視範圍和緩存範圍以外的Fragment,它的視圖將會被銷燬,可是它的實例將會保留在內存中,因此每一頁的Fragment至始至終都只須要構造一次而已。一般是在主頁中使用FragmentPagerAdapter, 可是超出範圍的Fragment的視圖會被銷燬,咱們也能夠在Fragment中緩存View來避免狀態的丟失,也可使用另外的機制,如緩存View的狀態。
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
    
        final long itemId = getItemId(position);
    
        // Do we already have this fragment?
        String name = makeFragmentName(container.getId(), itemId);
        Fragment fragment = mFragmentManager.findFragmentByTag(name);
        if (fragment != null) {
            if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
            mCurTransaction.attach(fragment);
        } else {
            fragment = getItem(position);
            if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
            mCurTransaction.add(container.getId(), fragment,
                    makeFragmentName(container.getId(), itemId));
        }
        if (fragment != mCurrentPrimaryItem) {
            fragment.setMenuVisibility(false);
            fragment.setUserVisibleHint(false);
        }
    
        return fragment;
    }
    
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object
                + " v=" + ((Fragment)object).getView());
        mCurTransaction.detach((Fragment)object);
    }
    複製代碼
  • 從上面源碼能夠得出結論
    • 當被銷燬時,Fragment並無從FragmentTransition中移除,而是調用了FragmentTransition.detach(Fragment)方法,這樣銷燬了Fragment的視圖,可是沒有移除Fragment自己。
    • detach:對應執行的是Fragment生命週期中onPause()-onDestroyView()的方法,此時並無執行onDestroy和onDetach方法。因此在恢復時只須要attach方法便可(能夠在FragmentPagerAdapter的instantiateItem方法中看到調用,對應源碼下面給出),attach方法對應的是執行Fragment生命週期中onCreateView()-onResume()。

6.2 FragmentStatePagerAdapter

  • FragmentStatePagerAdapter:會銷燬再也不須要的 fragment,噹噹前事務提交之後,會完全的將 fragmeng 從當前 Activity 的FragmentManager 中移除,state 標明,銷燬時,會將其 onSaveInstanceState(Bundle outState) 中的 bundle 信息保存下來,當用戶切換回來,能夠經過該 bundle 恢復生成新的 fragment,也就是說,你能夠在 onSaveInstanceState(Bundle outState) 方法中保存一些數據,在 onCreate 中進行恢復建立。
    • 使用 FragmentStatePagerAdapter 更省內存,可是銷燬後新建也是須要時間的。通常狀況下,若是你是製做主頁面,就 三、4 個 Tab,那麼能夠選擇使用 FragmentPagerAdapter,若是你是用於 ViewPager 展現數量特別多的條目時,那麼建議使用 FragmentStatePagerAdapter。
  • PagerAdapter的實現類,使用Fragment來管理每一頁。這個類也會管理保存和恢復Fragment的狀態。
    • 當使用一個大數量頁面時,FragmentStatePagerAdapter將更加有用,工做機制相似於ListView. 當每頁再也不可見時,整個Fragment將會被銷燬,只保留Fragment的狀態。相對於FragmentPagerAdapter, 這個將容許頁面持有更少的內存。
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        // If we already have this item instantiated, there is nothing
        // to do.  This can happen when we are restoring the entire pager
        // from its saved state, where the fragment manager has already
        // taken care of restoring the fragments we previously had instantiated.
        if (mFragments.size() > position) {
            Fragment f = mFragments.get(position);
            if (f != null) {
                return f;
            }
        }
    
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
    
        Fragment fragment = getItem(position);
        if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
        if (mSavedState.size() > position) {
            Fragment.SavedState fss = mSavedState.get(position);
            if (fss != null) {
                fragment.setInitialSavedState(fss);
            }
        }
        while (mFragments.size() <= position) {
            mFragments.add(null);
        }
        fragment.setMenuVisibility(false);
        fragment.setUserVisibleHint(false);
        mFragments.set(position, fragment);
        mCurTransaction.add(container.getId(), fragment);
    
        return fragment;
    }
    
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment) object;
    
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
                + " v=" + ((Fragment)object).getView());
        while (mSavedState.size() <= position) {
            mSavedState.add(null);
        }
        mSavedState.set(position, fragment.isAdded()
                ? mFragmentManager.saveFragmentInstanceState(fragment) : null);
        mFragments.set(position, null);
    
        mCurTransaction.remove(fragment);
    }
    複製代碼
    • 從源碼能夠看出,當銷燬Fragment時,緩存了Fragment的狀態,並移除了Fragment的引用。而在構造時,顯示判斷是否已經在構造,若是是則直接返回該Fragment, 若是不是,則從新構造一個新的Fragment, 而且若是已經緩存了狀態,則將改狀態傳入Fragment用於恢復狀態。

07.三種Adapter的總結

  • 三種Adapter的緩存策略
    • PagerAdapter:緩存三個,經過重寫instantiateItem和destroyItem達到建立和銷燬view的目的。
    • FragmentPagerAdapter:內部經過FragmentManager來持久化每個Fragment,在destroyItem方法調用時只是detach對應的Fragment,並無真正移除!
    • FragmentPagerStateAdapter:內部經過FragmentManager來管理每個Fragment,在destroyItem方法,調用時移除對應的Fragment。
  • 三個Adapter使用場景分析
    • PagerAdapter:當所要展現的視圖比較簡單時適用
    • FragmentPagerAdapter:當所要展現的視圖是Fragment,而且數量比較少時適用
    • FragmentStatePagerAdapter:當所要展現的視圖是Fragment,而且數量比較多時適用

其餘介紹

01.關於博客彙總連接

02.關於個人博客

狀態管理器項目地址:github.com/yangchong21…

自定義PagerAdapter輪播圖案例:github.com/yangchong21…

相關文章
相關標籤/搜索