成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專(zhuān)欄INFORMATION COLUMN

PagerAdapter深度解析和實(shí)踐優(yōu)化

Paul_King / 1147人閱讀

摘要:如果是那么在初始狀態(tài)下,默認(rèn)會(huì)出現(xiàn)前兩個(gè)頁(yè)面,而主頁(yè)面是在的起始位置通常是屏幕左側(cè),直到最后一個(gè)頁(yè)面在屏幕右側(cè),如果總共個(gè)頁(yè)面,返回值為那么將一次性出現(xiàn)所有的頁(yè)面用于數(shù)據(jù)刷新時(shí)的頁(yè)面處理方式。

目錄介紹

01.PagerAdapter簡(jiǎn)單介紹
02.PagerAdapter抽象方法
03.PagerAdapter原理介紹
04.PagerAdapter緩存和銷(xiāo)毀
05.自定義PagerAdapter
06.PagerAdapter兩個(gè)子類(lèi)
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抽象方法

子類(lèi)繼承PagerAdapter需要實(shí)現(xiàn)方法說(shuō)明
Object instantiateItem(ViewGroup container, int position)
一句話:要顯示的頁(yè)面或需要緩存的頁(yè)面,會(huì)調(diào)用這個(gè)方法進(jìn)行布局的初始化。
這個(gè)方法是ViewPager需要加載某個(gè)頁(yè)面時(shí)調(diào)用,container就是ViewPager自己,position頁(yè)面索引;
我們需要實(shí)現(xiàn)的是添加一個(gè)view到container中,然后返回一個(gè)跟這個(gè)view能夠關(guān)聯(lián)起來(lái)的對(duì)象,這個(gè)對(duì)象可以是view自身,也可以是其他對(duì)象(比如FragmentPagerAdapter返回的就是一個(gè)Fragment),關(guān)鍵是在isViewFromObject能夠?qū)iew和這個(gè)object關(guān)聯(lián)起來(lái)
void destroyItem(ViewGroup container, int position, Object object)
一句話:當(dāng)ViewPager需要銷(xiāo)毀一個(gè)頁(yè)面時(shí)調(diào)用,我們需要將position對(duì)應(yīng)的view從container中移除。
這時(shí)參數(shù)除了position就只有object,其實(shí)就是上面instantiateItem方法返回的對(duì)象,這時(shí)要通過(guò)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;。
從名稱(chēng)理解起來(lái)像是判斷view是否來(lái)自object,跟進(jìn)一步解釋?xiě)?yīng)該是上面instantiateItem方法中
向container中添加的view和方法返回的對(duì)象兩者之間一對(duì)一的關(guān)系;因?yàn)樵赩iewPager內(nèi)部有個(gè)方法叫infoForChild,
這個(gè)方法是通過(guò)view去找到對(duì)應(yīng)頁(yè)面信息緩存類(lèi)ItemInfo(內(nèi)部調(diào)用了isViewFromObject),如果找不到,說(shuō)明這個(gè)view是個(gè)野孩子,ViewPager會(huì)認(rèn)為不是Adapter提供的View,所以這個(gè)View不會(huì)顯示出來(lái);
總結(jié)一下:isViewFromObject 方法是讓view和object(內(nèi)部為ItemInfo)一一對(duì)應(yīng)起來(lái)
int getItemPosition(Object object)
改方法是判斷當(dāng)前object對(duì)應(yīng)的View是否需要更新,在調(diào)用notifyDataSetChanged時(shí)會(huì)間接觸發(fā)該方法,
如果返回POSITION_UNCHANGED表示該頁(yè)面不需要更新,如果返回POSITION_NONE則表示該頁(yè)面無(wú)效了,需要銷(xiāo)毀并觸發(fā)destroyItem方法(并且有可能調(diào)用instantiateItem重新初始化這個(gè)頁(yè)面)
02.PagerAdapter原理介紹

ViewPager+PagerAdapter的合作關(guān)系:
ViewPager來(lái)控制一頁(yè)界面構(gòu)造和銷(xiāo)毀的時(shí)機(jī),使用回調(diào)來(lái)通知PagerAdapter具體做什么,PagerAdapter只需要按照相應(yīng)的步驟做。當(dāng)然為了使用得更好、提供更多的功能,又建議了使用View的回收工作和管理工作,同時(shí)提供當(dāng)數(shù)據(jù)改變時(shí)的界面刷新工作。
instantiateItem(ViewGroup, int):
構(gòu)造指定位置的頁(yè)面。adapter負(fù)責(zé)在這個(gè)方法中添加view到容器中,即使是在finishUpdate(ViewGroup)才保證完成的。在FragmentPagerAdapter和FragmentStatePagerAdapter中,都是返回一個(gè)構(gòu)造的Fragment.
destroyItem(ViewGroup, populate, Object):
移除指定位置的頁(yè)面。adapter負(fù)責(zé)從容器中移除view,即是最后實(shí)在finishUpdate(ViewGroup)保證完成的。在FragmentPagerAdapter和FragmentStatePagerAdapter中,分別使用FragmentTransition.detach(Fragment)和FragmentTransition.remove(Fragment)來(lái)邏輯上銷(xiāo)毀Fragment.
finishUpdate(ViewGroup):
當(dāng)頁(yè)面的顯示變化完成式調(diào)用。在這里,你一定保證所有的頁(yè)面從容器中合理的添加或移除掉。
setPrimaryItem(ViewGroup, int, Object):
被ViewPager調(diào)用來(lái)通知adapter此時(shí)那個(gè)item應(yīng)該被認(rèn)為是主要的頁(yè)面,這個(gè)頁(yè)面將在當(dāng)前頁(yè)面展示給用戶(hù)。正是因?yàn)檫@個(gè)方法,才有在ViewPager中實(shí)現(xiàn)Fragment懶加載的機(jī)制。
isViewFromObject(View, Object):
指定當(dāng)前頁(yè)面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ì)究,不過(guò)還是一次性分析完為好,如getPageTitle(int), getPageWidth(int), getItemPosition(Object)等。
getPageTitle(int): 返回每頁(yè)的標(biāo)題,多用于關(guān)聯(lián)indicator
getPageWidth(int): 返回指定的頁(yè)面相對(duì)于ViewPager寬度的比例,范圍(0.f-1.f]。默認(rèn)值為1.f, 即占滿(mǎn)整個(gè)屏幕。如果是0.5f, 那么在初始狀態(tài)下,默認(rèn)會(huì)出現(xiàn)前兩個(gè)頁(yè)面,而primary主頁(yè)面是在ViewPager的起始位置(通常是屏幕左側(cè)),直到最后一個(gè)頁(yè)面在屏幕右側(cè),如果總共5個(gè)頁(yè)面,返回值為0.2f, 那么將一次性出現(xiàn)所有的頁(yè)面.
getItemPosition(Object):
用于數(shù)據(jù)刷新時(shí)的頁(yè)面處理方式。返回值包括三類(lèi):POSITION_UNCHANGED表示位置沒(méi)有變化,即在添加或移除一頁(yè)或多頁(yè)之后該位置的頁(yè)面保持不變,可以用于一個(gè)ViewPager中最后幾頁(yè)的添加或移除時(shí),保持前幾頁(yè)仍然不變;POSITION_NONE,表示當(dāng)前頁(yè)不再作為ViewPager的一頁(yè)數(shù)據(jù),將被銷(xiāo)毀,可以用于無(wú)視View緩存的刷新;根據(jù)傳過(guò)來(lái)的參數(shù)Object來(lái)判斷這個(gè)key所指定的新的位置
04.PagerAdapter緩存和銷(xiāo)毀

在ViewPager三種Adapter的子view創(chuàng)建和銷(xiāo)毀的方法添加相關(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翻頁(yè),觀察控制臺(tái)的輸出,三種Adapter針對(duì)不同界面、不同滑動(dòng)方向的翻頁(yè)情況打印如下:

從圖中我們可以看到,三種Adapter在相同的情況下,ViewPager的子頁(yè)面銷(xiāo)毀和創(chuàng)建時(shí)機(jī)是一樣。通常所聽(tīng)到的都是FragmentPagerAdapter會(huì)緩存所有的Fragment子項(xiàng),而上圖中我們看到的是在滑動(dòng)的過(guò)程中它的destroyItem方法被調(diào)用了,而在滑動(dòng)回來(lái)時(shí)相對(duì)應(yīng)的子項(xiàng)Fragment也確實(shí)調(diào)用instantiateItem方法。這樣看來(lái)根本就沒(méi)有緩存……
但是仔細(xì)對(duì)比了一下三個(gè)Adapter創(chuàng)建視圖的過(guò)程,發(fā)現(xiàn)上面推論有所欠缺。
因?yàn)樵谑褂肍ragment作為子視圖時(shí),我們是通過(guò)getItem方法返回Fragment的,單純從這里打印instantiateItem的調(diào)用不代表Fragment真的完全被重新創(chuàng)建了(重新創(chuàng)建代表需要重新add,即從頭走一遍生命周期,但是在這里不能證明),也可以通過(guò)兩個(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é)果如下:

通過(guò)上圖我們可以看到,F(xiàn)ragmentPagerAdapter在最后向右邊劃回來(lái)時(shí)并沒(méi)有調(diào)用getItem方法(getItem是創(chuàng)建一個(gè)新的Fragment),這也就說(shuō)明了他沒(méi)有重新創(chuàng)建Fragment,證明了它會(huì)緩存所有Fragment,那么它到底在哪里做了緩存呢?具體看FragmentPagerAdapter分析……
05.自定義PagerAdapter

比如,引導(dǎo)頁(yè)使用ViewPager,這個(gè)時(shí)候動(dòng)態(tài)管理的Adapter,可以每次都會(huì)創(chuàng)建新view,銷(xiāo)毀舊View。節(jié)省內(nèi)存消耗性能??梢哉f(shuō)下面這種用的最多……
/**

@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,銷(xiāo)毀舊View。節(jié)省內(nèi)存消耗性能

revise: 比如使用場(chǎng)景是啟動(dòng)引導(dǎo)頁(yè)

*/
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);

}
比如,常見(jiàn)有無(wú)限輪播圖,可以自動(dòng)輪播,大家應(yīng)該用的特別多。這個(gè)時(shí)候可以?xún)?yōu)化自定義輪播圖的PagerAdapter,創(chuàng)建集合用來(lái)存儲(chǔ)view,再次用的時(shí)候先取集合,沒(méi)有就創(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;
/**
 * 用來(lái)存放View的集合
 */
private ArrayList mViewList = new ArrayList<>();
/**
 * 刷新全部
 */
@Override
public void notifyDataSetChanged() {
    mViewList.clear();
    initPosition();
    super.notifyDataSetChanged();
}

/**
 * 獲取item索引
 *
 * POSITION_UNCHANGED表示位置沒(méi)有變化,即在添加或移除一頁(yè)或多頁(yè)之后該位置的頁(yè)面保持不變,
 * 可以用于一個(gè)ViewPager中最后幾頁(yè)的添加或移除時(shí),保持前幾頁(yè)仍然不變;
 *
 * POSITION_NONE,表示當(dāng)前頁(yè)不再作為ViewPager的一頁(yè)數(shù)據(jù),將被銷(xiāo)毀,可以用于無(wú)視View緩存的刷新;
 * 根據(jù)傳過(guò)來(lái)的參數(shù)Object來(lái)判斷這個(gè)key所指定的新的位置
 * @param object                        objcet
 * @return
 */
@Override
public int getItemPosition(@NonNull Object object) {
    return POSITION_NONE;
}

/**
 * 注冊(cè)數(shù)據(jù)觀察者監(jiān)聽(tīng)
 * @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;
}

/**
 * 如果頁(yè)面不是當(dāng)前顯示的頁(yè)面也不是要緩存的頁(yè)面,會(huì)調(diào)用這個(gè)方法,將頁(yè)面銷(xiāo)毀。
 * @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","銷(xiāo)毀的方法");
}

/**
 *  要顯示的頁(yè)面或需要緩存的頁(yè)面,會(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 ArrayList 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","銷(xiāo)毀的方法");
}

@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)該都見(jiàn)到過(guò),那么自定義adpater的時(shí)候能否再優(yōu)化一下,ok,上面的方案剛好合適。如果有不同的想法,歡迎提出……該源代碼的開(kāi)源地址:https://github.com/yangchong2...
06.PagerAdapter兩個(gè)子類(lèi)

PagerAdapter 的兩個(gè)直接子類(lèi) FragmentPagerAdapter 和 FragmentStatePagerAdapter 。而我們常常會(huì)在 ViewPager 和 Fragment 結(jié)合使用的時(shí)候來(lái)使用這兩個(gè)適配器。
6.1 FragmentPagerAdapter

FragmentPagerAdapter 它將每一個(gè)頁(yè)面表示為一個(gè) Fragment,并且每一個(gè) Fragment 都將會(huì)保存到 FragmentManager 當(dāng)中。而且,當(dāng)用戶(hù)沒(méi)可能再次回到頁(yè)面的時(shí)候,F(xiàn)ragmentManager 才會(huì)將這個(gè) Fragment 銷(xiāo)毀。
FragmentPagerAdapter:對(duì)于不再需要的 fragment,選擇調(diào)用 onDetach() 方法,僅銷(xiāo)毀視圖,并不會(huì)銷(xiāo)毀 fragment 實(shí)例。
使用 FragmentPagerAdapter 需要實(shí)現(xiàn)兩個(gè)方法:
public Fragment getItem(int position) 返回的是對(duì)應(yīng)的 Fragment 實(shí)例,一般我們?cè)谑褂脮r(shí),會(huì)通過(guò)構(gòu)造傳入一個(gè)要顯示的 Fragment 的集合,我們只要在這里把對(duì)應(yīng)的 Fragment 返回就行了。
public int getCount() 這個(gè)上面介紹過(guò)了返回的是頁(yè)面的個(gè)數(shù),我們只要返回傳入集合的長(zhǎng)度就行了。
使用起來(lái)是非常簡(jiǎn)單的,F(xiàn)ragmentStatePagerAdapter 的使用也和上面一樣,那兩者到底有什么區(qū)別呢?
錯(cuò)誤說(shuō)法
超出范圍的Fragment會(huì)被銷(xiāo)毀。所以之前,我一直認(rèn)為的是,F(xiàn)ragmentPagerAdapter中通常最多會(huì)保留3個(gè)Fragment, 超出左右兩側(cè)的Fragment將被銷(xiāo)毀,滑動(dòng)到時(shí)又會(huì)被重新構(gòu)造。
PagerAdapter的實(shí)現(xiàn)類(lèi),使用將一直保留在FragmentManager中的Fragment來(lái)代表每一頁(yè),直到用戶(hù)返回上一頁(yè)。
當(dāng)用于典型地使用多靜態(tài)化的Fragment時(shí),F(xiàn)ragmentPagerAdapter無(wú)疑是最好使用的,例如一組tabs. 每個(gè)用戶(hù)訪問(wèn)過(guò)的頁(yè)面的Fragment都將會(huì)保留在內(nèi)存中,即使它的視圖層在不可見(jiàn)時(shí)已經(jīng)被銷(xiāo)毀。這可能導(dǎo)致使用比較大數(shù)量的內(nèi)存,因?yàn)镕ragment實(shí)例持有任意數(shù)量的狀態(tài)。如果使用大數(shù)據(jù)的頁(yè)面,考慮使用FragmentStatePagerAdapter.
從上面可以看出,即使是超出可視范圍和緩存范圍之外的Fragment,它的視圖將會(huì)被銷(xiāo)毀,但是它的實(shí)例將會(huì)保留在內(nèi)存中,所以每一頁(yè)的Fragment至始至終都只需要構(gòu)造一次而已。通常是在主頁(yè)中使用FragmentPagerAdapter, 但是超出范圍的Fragment的視圖會(huì)被銷(xiāo)毀,我們也可以在Fragment中緩存View來(lái)避免狀態(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)被銷(xiāo)毀時(shí),F(xiàn)ragment并沒(méi)有從FragmentTransition中移除,而是調(diào)用了FragmentTransition.detach(Fragment)方法,這樣銷(xiāo)毀了Fragment的視圖,但是沒(méi)有移除Fragment本身。
detach:對(duì)應(yīng)執(zhí)行的是Fragment生命周期中onPause()-onDestroyView()的方法,此時(shí)并沒(méi)有執(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ì)銷(xiāo)毀不再需要的 fragment,當(dāng)當(dāng)前事務(wù)提交以后,會(huì)徹底的將 fragmeng 從當(dāng)前 Activity 的FragmentManager 中移除,state 標(biāo)明,銷(xiāo)毀時(shí),會(huì)將其 onSaveInstanceState(Bundle outState) 中的 bundle 信息保存下來(lái),當(dāng)用戶(hù)切換回來(lái),可以通過(guò)該 bundle 恢復(fù)生成新的 fragment,也就是說(shuō),你可以在 onSaveInstanceState(Bundle outState) 方法中保存一些數(shù)據(jù),在 onCreate 中進(jìn)行恢復(fù)創(chuàng)建。
使用 FragmentStatePagerAdapter 更省內(nèi)存,但是銷(xiāo)毀后新建也是需要時(shí)間的。一般情況下,如果你是制作主頁(yè)面,就 3、4 個(gè) Tab,那么可以選擇使用 FragmentPagerAdapter,如果你是用于 ViewPager 展示數(shù)量特別多的條目時(shí),那么建議使用 FragmentStatePagerAdapter。
PagerAdapter的實(shí)現(xiàn)類(lèi),使用Fragment來(lái)管理每一頁(yè)。這個(gè)類(lèi)也會(huì)管理保存和恢復(fù)Fragment的狀態(tài)。
當(dāng)使用一個(gè)大數(shù)量頁(yè)面時(shí),F(xiàn)ragmentStatePagerAdapter將更加有用,工作機(jī)制類(lèi)似于ListView. 當(dāng)每頁(yè)不再可見(jiàn)時(shí),整個(gè)Fragment將會(huì)被銷(xiāo)毀,只保留Fragment的狀態(tài)。相對(duì)于FragmentPagerAdapter, 這個(gè)將允許頁(yè)面持有更少的內(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)銷(xiāo)毀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è),通過(guò)重寫(xiě)instantiateItem和destroyItem達(dá)到創(chuàng)建和銷(xiāo)毀view的目的。
FragmentPagerAdapter:內(nèi)部通過(guò)FragmentManager來(lái)持久化每一個(gè)Fragment,在destroyItem方法調(diào)用時(shí)只是detach對(duì)應(yīng)的Fragment,并沒(méi)有真正移除!
FragmentPagerStateAdapter:內(nèi)部通過(guò)FragmentManager來(lái)管理每一個(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.開(kāi)源項(xiàng)目匯總
3.生活博客匯總
4.喜馬拉雅音頻匯總
5.其他匯總
02.關(guān)于我的博客

github:https://github.com/yangchong211
知乎:https://www.zhihu.com/people/...
簡(jiǎn)書(shū):http://www.jianshu.com/u/b7b2...
csdn:http://my.csdn.net/m0_37700275
喜馬拉雅聽(tīng)書(shū):http://www.ximalaya.com/zhubo...
開(kāi)源中國(guó):https://my.oschina.net/zbj161...
泡在網(wǎng)上的日子:http://www.jcodecraeer.com/me...
郵箱:yangchong211@163.com
阿里云博客: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://m.hztianpu.com/yun/75670.html

相關(guān)文章

  • 反射改變TabLayout屬性

    摘要:第二種在原有基礎(chǔ)上通過(guò)繼承控件,重寫(xiě)其中幾個(gè)方法,并且通過(guò)反射來(lái)修改部分屬性,也能達(dá)到第一種方案效果。因此這里需要用反射替換成自己的滑動(dòng)監(jiān)聽(tīng),然后在的監(jiān)聽(tīng)類(lèi)中的方法,改變的顏色。通過(guò)反射找到源碼中成員變量,然后設(shè)置暴力訪問(wèn)權(quán)限。 目錄介紹 01.遇到的實(shí)際需求分析 02.原生TabLayout局限 03.TabLayout源碼解析 3.1 Tab選項(xiàng)卡如何實(shí)現(xiàn) 3.2 滑動(dòng)切換T...

    GeekGhc 評(píng)論0 收藏0
  • 性能優(yōu)化全新思路!實(shí)踐騰訊、字節(jié)、阿里、百度、網(wǎng)易等互聯(lián)網(wǎng)公司項(xiàng)目實(shí)戰(zhàn)+案例分析(附PDF源碼)

    摘要:不努力不奮斗,可能就會(huì)在基層一輩子止步不前。不過(guò),只一句,如果你還在做這一行,還是一名程序猿媛,想走上坡路的你,也許我這到手的十幾家一線互聯(lián)網(wǎng)公司性能優(yōu)化項(xiàng)目實(shí)戰(zhàn)可能會(huì)對(duì)你有所幫助。 ...

    ytwman 評(píng)論0 收藏0
  • javascript設(shè)計(jì)模式與開(kāi)發(fā)實(shí)踐全書(shū)深度解析(一)之單例模式

    摘要:所以程序在引入文件的時(shí)候用了單例模式,一個(gè)文件實(shí)例化一次,這種做法無(wú)疑是好的,但是也容易引起。在我們平時(shí)的開(kāi)發(fā)過(guò)程中,可以借鑒這兩種方式去緩存變量,節(jié)點(diǎn)等。 這一章作者講了一個(gè)例子,就是在用單例模式生成一個(gè)dom節(jié)點(diǎn),還要做到只有訪問(wèn)的時(shí)候才創(chuàng)建,后續(xù)訪問(wèn)直接用前面創(chuàng)建的。那么實(shí)際開(kāi)發(fā)中我們會(huì)用到這個(gè)模式嗎?現(xiàn)在我們基本都是用vue,react,angular開(kāi)發(fā),不太會(huì)直接去操作do...

    xioqua 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

Paul_King

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<