欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android中ViewPager你所不知道的優(yōu)化技巧分享

 更新時(shí)間:2024年04月22日 11:33:33   作者:圖靈1024  
提到ViewPager想必各位同學(xué)一點(diǎn)都不陌生,它是Android中最常用的組件之一,這篇文章小編就帶大家一起來(lái)看看ViewPager一些新的優(yōu)化方式吧

寫在前面

提到ViewPager想必各位同學(xué)一點(diǎn)都不陌生,它是Android中最常用的組件之一,一般配合Fragment一起使用。網(wǎng)上關(guān)于它的基本使用和常規(guī)優(yōu)化方式也有很多,在這里我就不一一贅述,而是直接進(jìn)入這篇文章的主題--ViewPager一些新的優(yōu)化方式

我獲得這項(xiàng)技能的背景

最近組里做新的Web容器的,一次承載多個(gè)H5頁(yè)面,以實(shí)現(xiàn)左右切換,默認(rèn)展示主會(huì)場(chǎng)頁(yè),并要達(dá)到提升打開(kāi)率的目標(biāo)。要達(dá)到這個(gè)目標(biāo),那勢(shì)必要從加載優(yōu)化入手,縮短頁(yè)面的打開(kāi)時(shí)間。 優(yōu)化的點(diǎn)包括但不限于,Activity初始化、ViewPager和Fragment的初始化、WebView的初始化等等。我做的第一個(gè)優(yōu)化點(diǎn)便是ViewPager相關(guān)。

解決ViewPager默認(rèn)加載多個(gè)Fragment的問(wèn)題

ViewPager會(huì)默認(rèn)給我們緩存多個(gè)Fragment,這樣設(shè)計(jì)的目的是為了提升左右滑動(dòng)的流暢度,代價(jià)就是會(huì)降低首次打開(kāi)的啟動(dòng)時(shí)間。這讓一個(gè)以打開(kāi)率為KPI的我來(lái)說(shuō)是不能容忍的!首先想到的解決方案便是懶加載,當(dāng)Fragment頁(yè)面可見(jiàn)時(shí),才從網(wǎng)絡(luò)加載數(shù)據(jù)并顯示出來(lái)。這樣做還是不能解決其它Fragment被緩存,以導(dǎo)致占用啟動(dòng)時(shí)間的問(wèn)題,那怎么辦?既然ViewPager不給我們只加載一個(gè)Fragment的機(jī)會(huì),那我們強(qiáng)行創(chuàng)造行不行。我首次只往Adapter塞一個(gè)Fragment,等加載完成后再調(diào)用notifyDataSetChanged方法更新其它頁(yè)面行不行!

解決重復(fù)刷新的問(wèn)題

FragmentPagerAdapter不會(huì)銷毀已經(jīng)初始化完畢的Fragment

那為什么會(huì)有重復(fù)刷新的問(wèn)題?且聽(tīng)我慢慢道來(lái)

我們的主會(huì)場(chǎng)在ViewPager中的位置是由后端下發(fā)的。首次加載單個(gè)Fragment,主會(huì)場(chǎng)在ViewPager中的位置只能是0,后續(xù)更新時(shí)根據(jù)后端下發(fā)的position動(dòng)態(tài)調(diào)整其所在的位置。

//調(diào)整主會(huì)場(chǎng)位置偽代碼
marketingInfoList.add(new MarketingInfo("www.juejin.com", "主會(huì)場(chǎng)"))
for (int i = 0; i <= 3; i++) {
    //將放在前兩個(gè)主會(huì)場(chǎng)前面
    if (i < 2) {
        marketingInfoList.add(i, new MarketingInfo("www.baidu.com", "模擬" + i));
    } else {
    //后兩個(gè)往主會(huì)場(chǎng)后面添加
        marketingInfoList.add(new MarketingInfo("www.baidu.com", "模擬" + i));
    }
}
mPagerAdapter.notifyDataSetChanged();
//重新設(shè)置選中主會(huì)場(chǎng)
mViewPager.setCurrentItem(2);

可在實(shí)際開(kāi)發(fā)的過(guò)程中卻發(fā)現(xiàn),主會(huì)場(chǎng)重復(fù)加載了兩次,ViewPager生成了一個(gè)新的Fragment去承載主會(huì)場(chǎng)。我們的用戶元?dú)鉂M滿的點(diǎn)開(kāi)我們的營(yíng)銷頁(yè),正準(zhǔn)備下單呢,頁(yè)面突然又重新白屏了一下。留下一句****,憤然離去。作為一名要給公司帶來(lái)增長(zhǎng)價(jià)值的開(kāi)發(fā)這是不能接受的!那怎么辦呢?分析源碼!

ViewPager源碼解析

instantiateItem方法作用

ViewPager會(huì)通過(guò)這個(gè)方法將構(gòu)造Fragment,F(xiàn)ragmentManager和Transaction都在這個(gè)方法里出現(xiàn)

public Object instantiateItem(ViewGroup container, int position) {
    if (mCurTransaction == null) {
        mCurTransaction = mFragmentManager.beginTransaction();
    }
    
    final long itemId = getItemId(position);

    //跟據(jù)itemId生成fragment名字,通過(guò)名字去查找fragment是否加載過(guò)
    String name = makeFragmentName(container.getId(), itemId);
    Fragment fragment = mFragmentManager.findFragmentByTag(name);
    //fragment加載過(guò)則直接attach,否則的話新生成一個(gè)fragment
    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;
 }

instantiateItem會(huì)通過(guò)getItemId獲取到itemId,再生成與fragment對(duì)應(yīng)的唯一tag,通過(guò)tag查找fragment是否加載過(guò)。也就是說(shuō)只要tag相同,無(wú)論你點(diǎn)擊的是哪個(gè)Tab都會(huì)加載到同一個(gè)fragment。我們?cè)俳又榭瓷蓆ag的方法makeFragmentName。

private static String makeFragmentName(int viewId, long id) {
    return "android:switcher:" + viewId + ":" + id;
}

原來(lái)Tag就是由instantiateItem傳入的viewId和itemId兩個(gè)值組成,那么我們?cè)倏纯磇temId的生成方式

public long getItemId(int position) {
    return position;
}

我驚了!更加的簡(jiǎn)單!也就是說(shuō)Fragment的唯一Tag是又position決定的。這下剛剛的問(wèn)題有答案了吧。

重復(fù)刷新的真相與解決

ViewPager在初始化Fragment時(shí),會(huì)根據(jù)Tag尋找Fragment,有則直接加載,無(wú)則重新生成。主會(huì)場(chǎng)首次加載的position是0,后續(xù)調(diào)整位置后變成了2,導(dǎo)致兩次的Tag不一至,所以就出現(xiàn)了重復(fù)加載的問(wèn)題。知道了問(wèn)題產(chǎn)生的原因,再來(lái)想解決辦法就好辦了。我們可以重寫getItem方法,重新定義itemId的生成方式。

 public long getItemId(int position) {
     //可以直接使用后端給頁(yè)面ID
     return pageId;
     //后端不給也沒(méi)事,我們自己生成一個(gè)
     return data.get(position).getTitle().hashCode();
 }

延伸: #getItemPosition方法

如果不重寫getItemId這方法,將頁(yè)面位置調(diào)整后再跳切回舊的位置,還會(huì)面臨就位置的頁(yè)面不刷新的問(wèn)題。舉個(gè)栗子:

掘金的position是0,我將它的position改為2,第0個(gè)position這個(gè)時(shí)候設(shè)置為百度,會(huì)發(fā)現(xiàn)首個(gè)頁(yè)面依然是掘金。

網(wǎng)上給出的答案是重寫getItemPosition方法,雖然可以解決問(wèn)題,但是沒(méi)有一個(gè)能講明白這個(gè)方法的作用,在這里我來(lái)補(bǔ)充一下

public int getItemPosition(Object object) {
        return POSITION_UNCHANGED;
}

getItemPosition默認(rèn)返回POSITION_UNCHANGED,表示頁(yè)面無(wú)變化。還有另外一個(gè)默認(rèn)值POSITION_NONE,表示頁(yè)面不存在。

???

頁(yè)面指的是哪個(gè)頁(yè)面?調(diào)用時(shí)機(jī)又是什么?還能再返回其它值嗎? 各位看官先別急且看我慢慢寫來(lái),寫帖一段源碼:

void dataSetChanged() {
        // This method only gets called if our observer is attached, so mAdapter is non-null.

        final int adapterCount = mAdapter.getCount();
        mExpectedAdapterCount = adapterCount;
        boolean needPopulate = mItems.size() < mOffscreenPageLimit * 2 + 1
                && mItems.size() < adapterCount;
        int newCurrItem = mCurItem;

        boolean isUpdating = false;
            //mItems為舊數(shù)據(jù)的容器
        for (int i = 0; i < mItems.size(); i++) {
            final ItemInfo ii = mItems.get(i);
          //返回刷新之前Tab項(xiàng)所處的位置
            final int newPos = mAdapter.getItemPosition(ii.object);
            //返回的位置等于POSITION_UNCHANGED(-1)表示當(dāng)前頁(yè)未有變更,不做任何操作
            if (newPos == PagerAdapter.POSITION_UNCHANGED) {
                continue;
            }
            //如果返回的位置等于POSITION_NONE(-2)表示當(dāng)前頁(yè)Tab項(xiàng)刷新后不存在,需要銷毀并重新加載新的頁(yè)面
            if (newPos == PagerAdapter.POSITION_NONE) {
                mItems.remove(i);
                i--;

                if (!isUpdating) {
                    mAdapter.startUpdate(this);
                    isUpdating = true;
                }

                mAdapter.destroyItem(this, ii.position, ii.object);
                needPopulate = true;

                if (mCurItem == ii.position) {
                    // Keep the current item in the valid range
                    newCurrItem = Math.max(0, Math.min(mCurItem, adapterCount - 1));
                    needPopulate = true;
                }
                continue;
            }
            //如果當(dāng)前頁(yè)的新的位置和和舊位置不等,則說(shuō)明調(diào)整了順序
            if (ii.position != newPos) {
            //這段代碼是將頁(yè)面定位到刷新之前的打開(kāi)頁(yè),據(jù)數(shù)據(jù)的position和mCurItem相等的話,則表示這個(gè)item是之前打開(kāi)的,賦予它新位置的值
                if (ii.position == mCurItem) {
                    // Our current item changed position. Follow it.
                    newCurrItem = newPos;
                }

                ii.position = newPos;
                needPopulate = true;
            }
        }

        if (isUpdating) {
            mAdapter.finishUpdate(this);
        }

        Collections.sort(mItems, COMPARATOR);

        if (needPopulate) {
            // Reset our known page widths; populate will recompute them.
            final int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                final View child = getChildAt(i);
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                if (!lp.isDecor) {
                    lp.widthFactor = 0.f;
                }
            }

            setCurrentItemInternal(newCurrItem, false, true);
            requestLayout();
        }
    }

notifyDataSetChanged方法之后會(huì)調(diào)用dataSetChanged方法,getItemPosition又是在dataSetChanged方法被調(diào)用的。

調(diào)用notifyDataSetChanged的后,會(huì)遍歷舊的頁(yè)面,通過(guò)getItemPosition方法返回的位置去決定當(dāng)前遍歷到的頁(yè)面是否需要更新。POSITION_UNCHANGED:表示頁(yè)面無(wú)變化;POSITION_NONE:表示頁(yè)面不存在,需要銷毀,重新加載新的頁(yè)面。如果返回值返回的是頁(yè)面具體的位置,則更新當(dāng)前頁(yè)在刷新數(shù)據(jù)后的位置,將Tab欄選中的對(duì)應(yīng)的Tab項(xiàng)選中。

以上就是Android中ViewPager你所不知道的優(yōu)化技巧分享的詳細(xì)內(nèi)容,更多關(guān)于Android ViewPager優(yōu)化的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論