摘要:如果是那么在初始狀態(tài)下,默認(rèn)會(huì)出現(xiàn)前兩個(gè)頁面,而主頁面是在的起始位置通常是屏幕左側(cè),直到最后一個(gè)頁面在屏幕右側(cè),如果總共個(gè)頁面,返回值為那么將一次性出現(xiàn)所有的頁面用于數(shù)據(jù)刷新時(shí)的頁面處理方式。
目錄介紹
01.PagerAdapter簡(jiǎn)單介紹
02.PagerAdapter抽象方法
03.PagerAdapter原理介紹
04.PagerAdapter緩存和銷毀
05.自定義PagerAdapter
06.PagerAdapter兩個(gè)子類
07.三種Adapter的總結(jié)
00.ViewPager相關(guān)
ViewPager懶加載:https://juejin.im/post/5d37bb...
這篇博客是接著上一篇繼續(xù)分析和實(shí)踐優(yōu)化的。
01.PagerAdapter簡(jiǎn)單介紹
使用場(chǎng)景
輪播圖:ViewPager+自定義PagerAdapter
fragment:TabLayout+ViewPager+FragmentPagerAdapter+Fragment
02.PagerAdapter抽象方法
子類繼承PagerAdapter需要實(shí)現(xiàn)方法說明
Object instantiateItem(ViewGroup container, int position)
一句話:要顯示的頁面或需要緩存的頁面,會(huì)調(diào)用這個(gè)方法進(jìn)行布局的初始化。
這個(gè)方法是ViewPager需要加載某個(gè)頁面時(shí)調(diào)用,container就是ViewPager自己,position頁面索引;
我們需要實(shí)現(xiàn)的是添加一個(gè)view到container中,然后返回一個(gè)跟這個(gè)view能夠關(guān)聯(lián)起來的對(duì)象,這個(gè)對(duì)象可以是view自身,也可以是其他對(duì)象(比如FragmentPagerAdapter返回的就是一個(gè)Fragment),關(guān)鍵是在isViewFromObject能夠?qū)iew和這個(gè)object關(guān)聯(lián)起來
void destroyItem(ViewGroup container, int position, Object object)
一句話:當(dāng)ViewPager需要銷毀一個(gè)頁面時(shí)調(diào)用,我們需要將position對(duì)應(yīng)的view從container中移除。
這時(shí)參數(shù)除了position就只有object,其實(shí)就是上面instantiateItem方法返回的對(duì)象,這時(shí)要通過object找到對(duì)應(yīng)的View,然后將其移除掉,如果你的instantiateItem方法返回的就是View,這里就直接強(qiáng)轉(zhuǎn)成View移除即可:container.removeView((View) object);如果不是,一般會(huì)自己創(chuàng)建一個(gè)List緩存view列表,然后根據(jù)position從List中找到對(duì)應(yīng)的view移除;(當(dāng)然你也可以不移除,內(nèi)存泄漏)。
FragmentPagerAdapter的實(shí)現(xiàn)是:mCurTransaction.detach((Fragment)object),其實(shí)也就是將fragemnt的view從container中移除
isViewFromObject(View view, Object object)
一句話:這個(gè)方法用于判斷是否由對(duì)象生成界面,官方建議直接返回 return view == object;。
從名稱理解起來像是判斷view是否來自object,跟進(jìn)一步解釋應(yīng)該是上面instantiateItem方法中
向container中添加的view和方法返回的對(duì)象兩者之間一對(duì)一的關(guān)系;因?yàn)樵赩iewPager內(nèi)部有個(gè)方法叫infoForChild,
這個(gè)方法是通過view去找到對(duì)應(yīng)頁面信息緩存類ItemInfo(內(nèi)部調(diào)用了isViewFromObject),如果找不到,說明這個(gè)view是個(gè)野孩子,ViewPager會(huì)認(rèn)為不是Adapter提供的View,所以這個(gè)View不會(huì)顯示出來;
總結(jié)一下:isViewFromObject 方法是讓view和object(內(nèi)部為ItemInfo)一一對(duì)應(yīng)起來
int getItemPosition(Object object)
改方法是判斷當(dāng)前object對(duì)應(yīng)的View是否需要更新,在調(diào)用notifyDataSetChanged時(shí)會(huì)間接觸發(fā)該方法,
如果返回POSITION_UNCHANGED表示該頁面不需要更新,如果返回POSITION_NONE則表示該頁面無效了,需要銷毀并觸發(fā)destroyItem方法(并且有可能調(diào)用instantiateItem重新初始化這個(gè)頁面)
02.PagerAdapter原理介紹
ViewPager+PagerAdapter的合作關(guān)系:
ViewPager來控制一頁界面構(gòu)造和銷毀的時(shí)機(jī),使用回調(diào)來通知PagerAdapter具體做什么,PagerAdapter只需要按照相應(yīng)的步驟做。當(dāng)然為了使用得更好、提供更多的功能,又建議了使用View的回收工作和管理工作,同時(shí)提供當(dāng)數(shù)據(jù)改變時(shí)的界面刷新工作。
instantiateItem(ViewGroup, int):
構(gòu)造指定位置的頁面。adapter負(fù)責(zé)在這個(gè)方法中添加view到容器中,即使是在finishUpdate(ViewGroup)才保證完成的。在FragmentPagerAdapter和FragmentStatePagerAdapter中,都是返回一個(gè)構(gòu)造的Fragment.
destroyItem(ViewGroup, populate, Object):
移除指定位置的頁面。adapter負(fù)責(zé)從容器中移除view,即是最后實(shí)在finishUpdate(ViewGroup)保證完成的。在FragmentPagerAdapter和FragmentStatePagerAdapter中,分別使用FragmentTransition.detach(Fragment)和FragmentTransition.remove(Fragment)來邏輯上銷毀Fragment.
finishUpdate(ViewGroup):
當(dāng)頁面的顯示變化完成式調(diào)用。在這里,你一定保證所有的頁面從容器中合理的添加或移除掉。
setPrimaryItem(ViewGroup, int, Object):
被ViewPager調(diào)用來通知adapter此時(shí)那個(gè)item應(yīng)該被認(rèn)為是主要的頁面,這個(gè)頁面將在當(dāng)前頁面展示給用戶。正是因?yàn)檫@個(gè)方法,才有在ViewPager中實(shí)現(xiàn)Fragment懶加載的機(jī)制。
isViewFromObject(View, Object):
指定當(dāng)前頁面View是否和指定的key對(duì)象相關(guān)聯(lián)(這個(gè)key對(duì)象是在instantiateItem(ViewGroup, int)方法返回的)。這個(gè)方法需要PagerAdapter恰當(dāng)?shù)膶?shí)現(xiàn)。即只要匹配好鍵值對(duì)即可。FragmentPagerAdapter和FragmentStatePagerAdapter的實(shí)現(xiàn): return ((Fragment)object).getView() == view;.
雖然簡(jiǎn)單或很少使用到的一些方法不想細(xì)究,不過還是一次性分析完為好,如getPageTitle(int), getPageWidth(int), getItemPosition(Object)等。
getPageTitle(int): 返回每頁的標(biāo)題,多用于關(guān)聯(lián)indicator
getPageWidth(int): 返回指定的頁面相對(duì)于ViewPager寬度的比例,范圍(0.f-1.f]。默認(rèn)值為1.f, 即占滿整個(gè)屏幕。如果是0.5f, 那么在初始狀態(tài)下,默認(rèn)會(huì)出現(xiàn)前兩個(gè)頁面,而primary主頁面是在ViewPager的起始位置(通常是屏幕左側(cè)),直到最后一個(gè)頁面在屏幕右側(cè),如果總共5個(gè)頁面,返回值為0.2f, 那么將一次性出現(xiàn)所有的頁面.
getItemPosition(Object):
用于數(shù)據(jù)刷新時(shí)的頁面處理方式。返回值包括三類:POSITION_UNCHANGED表示位置沒有變化,即在添加或移除一頁或多頁之后該位置的頁面保持不變,可以用于一個(gè)ViewPager中最后幾頁的添加或移除時(shí),保持前幾頁仍然不變;POSITION_NONE,表示當(dāng)前頁不再作為ViewPager的一頁數(shù)據(jù),將被銷毀,可以用于無視View緩存的刷新;根據(jù)傳過來的參數(shù)Object來判斷這個(gè)key所指定的新的位置
04.PagerAdapter緩存和銷毀
在ViewPager三種Adapter的子view創(chuàng)建和銷毀的方法添加相關(guān)的日志代碼,如下:
@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); //...省略部分代碼
}
滑動(dòng)ViewPager翻頁,觀察控制臺(tái)的輸出,三種Adapter針對(duì)不同界面、不同滑動(dòng)方向的翻頁情況打印如下:
從圖中我們可以看到,三種Adapter在相同的情況下,ViewPager的子頁面銷毀和創(chuàng)建時(shí)機(jī)是一樣。通常所聽到的都是FragmentPagerAdapter會(huì)緩存所有的Fragment子項(xiàng),而上圖中我們看到的是在滑動(dòng)的過程中它的destroyItem方法被調(diào)用了,而在滑動(dòng)回來時(shí)相對(duì)應(yīng)的子項(xiàng)Fragment也確實(shí)調(diào)用instantiateItem方法。這樣看來根本就沒有緩存……
但是仔細(xì)對(duì)比了一下三個(gè)Adapter創(chuàng)建視圖的過程,發(fā)現(xiàn)上面推論有所欠缺。
因?yàn)樵谑褂肍ragment作為子視圖時(shí),我們是通過getItem方法返回Fragment的,單純從這里打印instantiateItem的調(diào)用不代表Fragment真的完全被重新創(chuàng)建了(重新創(chuàng)建代表需要重新add,即從頭走一遍生命周期,但是在這里不能證明),也可以通過兩個(gè)FragmentAdapter中instantiateItem的實(shí)現(xiàn)證明(觀察getItem方法的調(diào)用條件),所以又在Fragment對(duì)應(yīng)的兩種Adapter的getItem中添加相應(yīng)的log代碼,如下:
@Override
public Fragment getItem(int position) {
Log.d("ccc", "getItem:" + position); return fragmentList.get(position);
}
針對(duì)不同情況,控制臺(tái)輸出結(jié)果如下:
通過上圖我們可以看到,F(xiàn)ragmentPagerAdapter在最后向右邊劃回來時(shí)并沒有調(diào)用getItem方法(getItem是創(chuàng)建一個(gè)新的Fragment),這也就說明了他沒有重新創(chuàng)建Fragment,證明了它會(huì)緩存所有Fragment,那么它到底在哪里做了緩存呢?具體看FragmentPagerAdapter分析……
05.自定義PagerAdapter
比如,引導(dǎo)頁使用ViewPager,這個(gè)時(shí)候動(dòng)態(tài)管理的Adapter,可以每次都會(huì)創(chuàng)建新view,銷毀舊View。節(jié)省內(nèi)存消耗性能??梢哉f下面這種用的最多……
/**
@author yangchong
blog : https://github.com/yangchong211
time : 2016/3/18
desc : 動(dòng)態(tài)管理的Adapter。概念參照{(diào)@link android.support.v4.app.FragmentPagerAdapter}
每次都會(huì)創(chuàng)建新view,銷毀舊View。節(jié)省內(nèi)存消耗性能
revise: 比如使用場(chǎng)景是啟動(dòng)引導(dǎo)頁
*/
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; } /** * 創(chuàng)建view * @param container container * @param position 索引 * @return */ public abstract View getView(ViewGroup container, int position);
}
比如,常見有無限輪播圖,可以自動(dòng)輪播,大家應(yīng)該用的特別多。這個(gè)時(shí)候可以優(yōu)化自定義輪播圖的PagerAdapter,創(chuàng)建集合用來存儲(chǔ)view,再次用的時(shí)候先取集合,沒有就創(chuàng)建。而不是頻繁創(chuàng)建視圖。
/**
@author yangchong
blog : https://github.com/yangchong211
time : 2016/3/18
desc : AbsLoopPagerAdapter
revise: 如果是自動(dòng)輪播圖的話就用這一個(gè)
*/
public abstract class AbsLoopPagerAdapter extends PagerAdapter {
private BannerView mViewPager; /** * 用來存放View的集合 */ private ArrayListmViewList = new ArrayList<>(); /** * 刷新全部 */ @Override public void notifyDataSetChanged() { mViewList.clear(); initPosition(); super.notifyDataSetChanged(); } /** * 獲取item索引 * * POSITION_UNCHANGED表示位置沒有變化,即在添加或移除一頁或多頁之后該位置的頁面保持不變, * 可以用于一個(gè)ViewPager中最后幾頁的添加或移除時(shí),保持前幾頁仍然不變; * * POSITION_NONE,表示當(dāng)前頁不再作為ViewPager的一頁數(shù)據(jù),將被銷毀,可以用于無視View緩存的刷新; * 根據(jù)傳過來的參數(shù)Object來判斷這個(gè)key所指定的新的位置 * @param object objcet * @return */ @Override public int getItemPosition(@NonNull Object object) { return POSITION_NONE; } /** * 注冊(cè)數(shù)據(jù)觀察者監(jiān)聽 * @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); } } } /** * 設(shè)置位置,利用反射實(shí)現(xiàn) * @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; } /** * 如果頁面不是當(dāng)前顯示的頁面也不是要緩存的頁面,會(huì)調(diào)用這個(gè)方法,將頁面銷毀。 * @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","銷毀的方法"); } /** * 要顯示的頁面或需要緩存的頁面,會(huì)調(diào)用這個(gè)方法進(jìn)行布局的初始化。 * @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","創(chuàng)建的方法"); return itemView; } /** * 這個(gè)是避免重復(fù)創(chuàng)建,如果集合中有,則取集合中的 * @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() { //設(shè)置最大輪播圖數(shù)量 ,如果是1那么就是1,不輪播;如果大于1則設(shè)置一個(gè)最大值,可以輪播 //return getRealCount(); return getRealCount()<=1?getRealCount(): Integer.MAX_VALUE; } /** * 獲取輪播圖數(shù)量 * @return 數(shù)量 */ public abstract int getRealCount(); /** * 創(chuàng)建view * @param container viewGroup * @param position 索引 * @return */ public abstract View getView(ViewGroup container, int position);
}
還有一種場(chǎng)景,靜態(tài)輪播圖,也就是不會(huì)自動(dòng)輪播,但是手指可以滑動(dòng),并且滑動(dòng)到第一張不能往左滑動(dòng),滑動(dòng)到最后一張不能向右滑動(dòng)。這種場(chǎng)景,view添加進(jìn)去就不管了,View就常在呢!
/**
@author yangchong
blog : https://github.com/yangchong211
time : 2016/3/18
desc : 靜態(tài)存儲(chǔ)的Adapter,概念參照{(diào)@link android.support.v4.app.FragmentStatePagerAdapter}
view添加進(jìn)去就不管了,View長(zhǎng)在,內(nèi)存不再
revise: 如果是靜態(tài)輪播圖就用這個(gè)
*/
public abstract class AbsStaticPagerAdapter extends PagerAdapter {
private ArrayListmViewList = 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","創(chuàng)建的方法"); 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);
}
這三種不同的使用場(chǎng)景,我們應(yīng)該都見到過,那么自定義adpater的時(shí)候能否再優(yōu)化一下,ok,上面的方案剛好合適。如果有不同的想法,歡迎提出……該源代碼的開源地址:https://github.com/yangchong2...
06.PagerAdapter兩個(gè)子類
PagerAdapter 的兩個(gè)直接子類 FragmentPagerAdapter 和 FragmentStatePagerAdapter 。而我們常常會(huì)在 ViewPager 和 Fragment 結(jié)合使用的時(shí)候來使用這兩個(gè)適配器。
6.1 FragmentPagerAdapter
FragmentPagerAdapter 它將每一個(gè)頁面表示為一個(gè) Fragment,并且每一個(gè) Fragment 都將會(huì)保存到 FragmentManager 當(dāng)中。而且,當(dāng)用戶沒可能再次回到頁面的時(shí)候,F(xiàn)ragmentManager 才會(huì)將這個(gè) Fragment 銷毀。
FragmentPagerAdapter:對(duì)于不再需要的 fragment,選擇調(diào)用 onDetach() 方法,僅銷毀視圖,并不會(huì)銷毀 fragment 實(shí)例。
使用 FragmentPagerAdapter 需要實(shí)現(xiàn)兩個(gè)方法:
public Fragment getItem(int position) 返回的是對(duì)應(yīng)的 Fragment 實(shí)例,一般我們?cè)谑褂脮r(shí),會(huì)通過構(gòu)造傳入一個(gè)要顯示的 Fragment 的集合,我們只要在這里把對(duì)應(yīng)的 Fragment 返回就行了。
public int getCount() 這個(gè)上面介紹過了返回的是頁面的個(gè)數(shù),我們只要返回傳入集合的長(zhǎng)度就行了。
使用起來是非常簡(jiǎn)單的,F(xiàn)ragmentStatePagerAdapter 的使用也和上面一樣,那兩者到底有什么區(qū)別呢?
錯(cuò)誤說法
超出范圍的Fragment會(huì)被銷毀。所以之前,我一直認(rèn)為的是,F(xiàn)ragmentPagerAdapter中通常最多會(huì)保留3個(gè)Fragment, 超出左右兩側(cè)的Fragment將被銷毀,滑動(dòng)到時(shí)又會(huì)被重新構(gòu)造。
PagerAdapter的實(shí)現(xiàn)類,使用將一直保留在FragmentManager中的Fragment來代表每一頁,直到用戶返回上一頁。
當(dāng)用于典型地使用多靜態(tài)化的Fragment時(shí),F(xiàn)ragmentPagerAdapter無疑是最好使用的,例如一組tabs. 每個(gè)用戶訪問過的頁面的Fragment都將會(huì)保留在內(nèi)存中,即使它的視圖層在不可見時(shí)已經(jīng)被銷毀。這可能導(dǎo)致使用比較大數(shù)量的內(nèi)存,因?yàn)镕ragment實(shí)例持有任意數(shù)量的狀態(tài)。如果使用大數(shù)據(jù)的頁面,考慮使用FragmentStatePagerAdapter.
從上面可以看出,即使是超出可視范圍和緩存范圍之外的Fragment,它的視圖將會(huì)被銷毀,但是它的實(shí)例將會(huì)保留在內(nèi)存中,所以每一頁的Fragment至始至終都只需要構(gòu)造一次而已。通常是在主頁中使用FragmentPagerAdapter, 但是超出范圍的Fragment的視圖會(huì)被銷毀,我們也可以在Fragment中緩存View來避免狀態(tài)的丟失,也可以使用另外的機(jī)制,如緩存View的狀態(tài)。
@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);
}
從上面源碼可以得出結(jié)論
當(dāng)被銷毀時(shí),F(xiàn)ragment并沒有從FragmentTransition中移除,而是調(diào)用了FragmentTransition.detach(Fragment)方法,這樣銷毀了Fragment的視圖,但是沒有移除Fragment本身。
detach:對(duì)應(yīng)執(zhí)行的是Fragment生命周期中onPause()-onDestroyView()的方法,此時(shí)并沒有執(zhí)行onDestroy和onDetach方法。所以在恢復(fù)時(shí)只需要attach方法即可(可以在FragmentPagerAdapter的instantiateItem方法中看到調(diào)用,對(duì)應(yīng)源碼下面給出),attach方法對(duì)應(yīng)的是執(zhí)行Fragment生命周期中onCreateView()-onResume()。
6.2 FragmentStatePagerAdapter
FragmentStatePagerAdapter:會(huì)銷毀不再需要的 fragment,當(dāng)當(dāng)前事務(wù)提交以后,會(huì)徹底的將 fragmeng 從當(dāng)前 Activity 的FragmentManager 中移除,state 標(biāo)明,銷毀時(shí),會(huì)將其 onSaveInstanceState(Bundle outState) 中的 bundle 信息保存下來,當(dāng)用戶切換回來,可以通過該 bundle 恢復(fù)生成新的 fragment,也就是說,你可以在 onSaveInstanceState(Bundle outState) 方法中保存一些數(shù)據(jù),在 onCreate 中進(jìn)行恢復(fù)創(chuàng)建。
使用 FragmentStatePagerAdapter 更省內(nèi)存,但是銷毀后新建也是需要時(shí)間的。一般情況下,如果你是制作主頁面,就 3、4 個(gè) Tab,那么可以選擇使用 FragmentPagerAdapter,如果你是用于 ViewPager 展示數(shù)量特別多的條目時(shí),那么建議使用 FragmentStatePagerAdapter。
PagerAdapter的實(shí)現(xiàn)類,使用Fragment來管理每一頁。這個(gè)類也會(huì)管理保存和恢復(fù)Fragment的狀態(tài)。
當(dāng)使用一個(gè)大數(shù)量頁面時(shí),F(xiàn)ragmentStatePagerAdapter將更加有用,工作機(jī)制類似于ListView. 當(dāng)每頁不再可見時(shí),整個(gè)Fragment將會(huì)被銷毀,只保留Fragment的狀態(tài)。相對(duì)于FragmentPagerAdapter, 這個(gè)將允許頁面持有更少的內(nèi)存。
@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);
}
從源碼可以看出,當(dāng)銷毀Fragment時(shí),緩存了Fragment的狀態(tài),并移除了Fragment的引用。而在構(gòu)造時(shí),顯示判斷是否已經(jīng)在構(gòu)造,如果是則直接返回該Fragment, 如果不是,則重新構(gòu)造一個(gè)新的Fragment, 并且如果已經(jīng)緩存了狀態(tài),則將改狀態(tài)傳入Fragment用于恢復(fù)狀態(tài)。
07.三種Adapter的總結(jié)
三種Adapter的緩存策略
PagerAdapter:緩存三個(gè),通過重寫instantiateItem和destroyItem達(dá)到創(chuàng)建和銷毀view的目的。
FragmentPagerAdapter:內(nèi)部通過FragmentManager來持久化每一個(gè)Fragment,在destroyItem方法調(diào)用時(shí)只是detach對(duì)應(yīng)的Fragment,并沒有真正移除!
FragmentPagerStateAdapter:內(nèi)部通過FragmentManager來管理每一個(gè)Fragment,在destroyItem方法,調(diào)用時(shí)移除對(duì)應(yīng)的Fragment。
三個(gè)Adapter使用場(chǎng)景分析
PagerAdapter:當(dāng)所要展示的視圖比較簡(jiǎn)單時(shí)適用
FragmentPagerAdapter:當(dāng)所要展示的視圖是Fragment,并且數(shù)量比較少時(shí)適用
FragmentStatePagerAdapter:當(dāng)所要展示的視圖是Fragment,并且數(shù)量比較多時(shí)適用
其他介紹
01.關(guān)于博客匯總鏈接
1.技術(shù)博客匯總
2.開源項(xiàng)目匯總
3.生活博客匯總
4.喜馬拉雅音頻匯總
5.其他匯總
02.關(guān)于我的博客
github:https://github.com/yangchong211
知乎:https://www.zhihu.com/people/...
簡(jiǎn)書:http://www.jianshu.com/u/b7b2...
csdn:http://my.csdn.net/m0_37700275
喜馬拉雅聽書:http://www.ximalaya.com/zhubo...
開源中國:https://my.oschina.net/zbj161...
泡在網(wǎng)上的日子:http://www.jcodecraeer.com/me...
郵箱:[email protected]
阿里云博客:https://yq.aliyun.com/users/a... 239.headeruserinfo.3.dT4bcV
segmentfault頭條:https://segmentfault.com/u/xi...
掘金:https://juejin.im/user/593943...
狀態(tài)管理器項(xiàng)目地址:https://github.com/yangchong2...
自定義PagerAdapter輪播圖案例:https://github.com/yangchong2...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/75670.html
摘要:第二種在原有基礎(chǔ)上通過繼承控件,重寫其中幾個(gè)方法,并且通過反射來修改部分屬性,也能達(dá)到第一種方案效果。因此這里需要用反射替換成自己的滑動(dòng)監(jiān)聽,然后在的監(jiān)聽類中的方法,改變的顏色。通過反射找到源碼中成員變量,然后設(shè)置暴力訪問權(quán)限。 目錄介紹 01.遇到的實(shí)際需求分析 02.原生TabLayout局限 03.TabLayout源碼解析 3.1 Tab選項(xiàng)卡如何實(shí)現(xiàn) 3.2 滑動(dòng)切換T...
摘要:不努力不奮斗,可能就會(huì)在基層一輩子止步不前。不過,只一句,如果你還在做這一行,還是一名程序猿媛,想走上坡路的你,也許我這到手的十幾家一線互聯(lián)網(wǎng)公司性能優(yōu)化項(xiàng)目實(shí)戰(zhàn)可能會(huì)對(duì)你有所幫助。 ...
摘要:所以程序在引入文件的時(shí)候用了單例模式,一個(gè)文件實(shí)例化一次,這種做法無疑是好的,但是也容易引起。在我們平時(shí)的開發(fā)過程中,可以借鑒這兩種方式去緩存變量,節(jié)點(diǎn)等。 這一章作者講了一個(gè)例子,就是在用單例模式生成一個(gè)dom節(jié)點(diǎn),還要做到只有訪問的時(shí)候才創(chuàng)建,后續(xù)訪問直接用前面創(chuàng)建的。那么實(shí)際開發(fā)中我們會(huì)用到這個(gè)模式嗎?現(xiàn)在我們基本都是用vue,react,angular開發(fā),不太會(huì)直接去操作do...
閱讀 2225·2019-08-30 15:53
閱讀 2454·2019-08-30 12:54
閱讀 1205·2019-08-29 16:09
閱讀 730·2019-08-29 12:14
閱讀 758·2019-08-26 10:33
閱讀 2483·2019-08-23 18:36
閱讀 2960·2019-08-23 18:30
閱讀 2121·2019-08-22 17:09