ViewPager預(yù)加載、懶加載和禁止左右滑動(dòng)

此筆記主要是說(shuō)一下ViewPager如何實(shí)現(xiàn)預(yù)加載,以及如何實(shí)現(xiàn)懶加載、禁止左右滑動(dòng)。

  • ViewPager 作為平時(shí)開(kāi)發(fā)時(shí)經(jīng)常使用的控件,我們多是配合TabLayout、嵌套Fragment使用。

預(yù)加載

  • 當(dāng)ViewPager中嵌套的Fragment多于2個(gè)的時(shí)候,ViewPager就會(huì)預(yù)加載當(dāng)前顯示Fragment左右兩側(cè)的Fragment。

ViewPager是如何實(shí)現(xiàn)預(yù)加載的?

翻看ViewPager我們可以發(fā)現(xiàn)一個(gè)常量 private static final int DEFAULT_OFFSCREEN_PAGES = 1; 其實(shí),這就是實(shí)現(xiàn)ViewPager預(yù)加載的值,這個(gè)值的意義也是 默認(rèn)ViewPager當(dāng)前變量的值為1。

那么ViewPager具體是如何實(shí)現(xiàn)的呢?繼續(xù)翻看源碼。

ViewPager源碼中全局搜查 DEFAULT_OFFSCREEN_PAGES,發(fā)現(xiàn)將 DEFAULT_OFFSCREEN_PAGES 賦值給了 mOffscreenPageLimit ,那我們繼續(xù)搜查 mOffscreenPageLimit ,發(fā)現(xiàn) 又賦值給了 pageLimit ,好了,我們終于找到了我們要看的邏輯。源碼中我們發(fā)現(xiàn),通過(guò)當(dāng)前的Item與pageLimit計(jì)算左右需要預(yù)加載頁(yè)面的角標(biāo)。

計(jì)算的方法如下:


    final int startPos = Math.max(0, mCurItem - pageLimit);
    final int N = mAdapter.getCount();
    final int endPos = Math.min(N - 1, mCurItem + pageLimit);

好了,我們來(lái)模擬一下,假設(shè)此時(shí)ViewPager中有10個(gè)頁(yè)面,當(dāng)前頁(yè)面為3。pageLimit = mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES = 1;

    startPos = Math.max(0 , 3 - 1); = Math.max(0 ,2); = 2;
    n = 10;
    endPos = Math.min(10 - 1 , 3 + 1); = Math.min(9 , 4); = 4;

    也就是說(shuō):

    startPos = 2;
    n = 10;
    endPos = 4;

    這樣就實(shí)現(xiàn)了ViewPager的預(yù)加載功能。

懶加載

ViewPager如何實(shí)現(xiàn)懶加載?

  • 上面我們分析了ViewPager是如何實(shí)現(xiàn)預(yù)加載的,可是有時(shí)我們不想實(shí)現(xiàn)ViewPager的預(yù)加載功能,因?yàn)橛脩艨赡懿粫?huì)查看預(yù)加載的頁(yè)面就退出了,而且預(yù)加載的頁(yè)面如果有聯(lián)網(wǎng)操作,也會(huì)消耗用戶的流量。
  • 那么,我們?nèi)绾螌?shí)現(xiàn)懶加載呢?

別怕,上面我們既然找到了ViewPager如何實(shí)現(xiàn)預(yù)加載的方法,我們可以修改 DEFAULT_OFFSCREEN_PAGES = 0 ,使其不實(shí)現(xiàn)預(yù)加載。我們可以驗(yàn)證一下:


    final int startPos = Math.max(0, mCurItem - pageLimit);
    final int N = mAdapter.getCount();
    final int endPos = Math.min(N - 1, mCurItem + pageLimit);

同樣,還假設(shè)ViewPager中有10個(gè)頁(yè)面,當(dāng)前頁(yè)面為第3個(gè)。pageLimit = mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES = 0;

    startPos = Math.max(0 , 3 - 0); = Math.max(0 ,3); = 3;
    n = 10;
    endPos = Math.min(10 - 1 , 3 + 0); = Math.min(9 , 3); = 3;

    也就是說(shuō):

    startPos = 3;
    n = 10;
    endPos = 3;

    這樣就實(shí)現(xiàn)了ViewPager的懶加載功能。

但是 DEFAULT_OFFSCREEN_PAGES =1; 是ViewPager中默認(rèn)的值,即便是我們打開(kāi)ViewPager的源碼將其修改為0,運(yùn)行后還是默認(rèn)為1 ,我們?nèi)绻獙?shí)現(xiàn)懶加載,只有將 ViewPager 下的代碼完全復(fù)制一份,然后自建一LazyViewPager 繼承于ViewGroup,然后將代碼粘貼過(guò)來(lái),并將 DEFAULT_OFFSCREEN_PAGES 的值修改為0即可,使用的時(shí)候,直接使用LazyViewPager即可。

ViewPager禁止左右滑動(dòng)

ViewPager如何禁止左右滑動(dòng)?

  • 我們?cè)谑褂肰iewPager的時(shí)候一般里面嵌套的Fragment會(huì)使用ListView或者RecyclerView 亦或者有輪播圖,但是在左右滑動(dòng)的時(shí)候是輪播圖滑動(dòng)呢還是讓ViewPager左右滑動(dòng)呢?
  • 這個(gè)時(shí)候我們就要禁止ViewPager的左右滑動(dòng)操作。我們?nèi)绾尾僮髂兀?/li>

其實(shí)禁止ViewPager的左右滑動(dòng)也很簡(jiǎn)單,從事件分發(fā)機(jī)制考慮即可。翻看ViewPager的 onInterceptTouchEvent 方法,也可以看到注釋:

public boolean onInterceptTouchEvent(MotionEvent ev){
    /*
     * This method JUST determines whether we want to intercept thmotion.
     * If we return true, onMotionEvent will be called and we do thactual
     * scrolling there.
     */
 
    ...
 }

什么意思呢?大概意思是說(shuō):這個(gè)方法決定了我們是否要截?cái)鄤?dòng)作。如果返回true,onMotionEvent將被調(diào)用,我們?cè)谀抢飯?zhí)行實(shí)際的滾動(dòng)。

所以,我們可以大概理解為:是否要攔截事件,如果攔截就自己處理,如果不攔截就將事件傳遞給下面的子View處理。我們可以發(fā)現(xiàn),此方法返回的是boolean類型,所以,我們要禁止左右滑動(dòng)的話,只需要返回false即可,意思就是不攔截事件,傳遞給下面的子View。

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return false;
    }
  • 但是如果viewpage里面子控件不是viewgroup,還是會(huì)調(diào)用 onTouchEvent 方法,所以,我們還要處理onTouchEvent這個(gè)方法。

查看ViewPager的onTouchEvent發(fā)現(xiàn),喔,里面處理的邏輯好多,看的頭蒙,怎么辦呢?不用急,簡(jiǎn)單的說(shuō)此方法大概意思是:是否自己消費(fèi)事件。如果自消費(fèi),事件就結(jié)束。如果不消費(fèi)就傳遞給父控件。所以,我們想實(shí)現(xiàn)禁止左右滑動(dòng),只需要ViewPager不消費(fèi)事件即可:

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        return false;
    }

綜上所述,如果我們想實(shí)現(xiàn)ViewPager的禁止左右滑動(dòng),只要覆寫 onInterceptTouchEvent 方法 和 onTouchEvent 方法即可。

  • 如果考慮復(fù)用,即使用同一個(gè)ViewPager ,有時(shí)可以左右滑動(dòng),有時(shí)禁止,那么我們可以寫一個(gè)方法讓其實(shí)現(xiàn)是否可以左右滑動(dòng)。
    public void setNoScroll(boolean noScroll) {
        mNoScoll = noScroll;
    }
    

然后在 onTouchEvent 方法和 onInterceptTouchEvent方法判斷處理即可:

 @Override
    public boolean onTouchEvent(MotionEvent ev) {

        if (mNoScoll) {
            return false;
        } else {
            return super.onTouchEvent(ev);
        }


    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (mNoScoll) {
            return false;
        } else {
            return super.onInterceptTouchEvent(ev);
        }
    }

簡(jiǎn)單說(shuō)一下,mNoScoll 默認(rèn)false ,如果我們現(xiàn)在的ViewPager 不想實(shí)現(xiàn)左右滑動(dòng),只需要 setNoScroll(true) 即可,此時(shí)mNoScoll 為false,然后在 onTouchEvent 方法和 onInterceptTouchEvent 方法的時(shí)候已經(jīng) 攔截 和 不消費(fèi)了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,388評(píng)論 25 708
  • 2017年05月30 最后的懶加載寫的不好,推薦請(qǐng)叫我大蘇同學(xué)寫的Fragment懶加載博客,【Android】再...
    英勇青銅5閱讀 13,579評(píng)論 56 189
  • 絲絲小雨臨江南,潤(rùn)色多少閑庭館。 百年老屋人何在?歲月變換如云煙。
    綠野V仙蹤閱讀 276評(píng)論 4 4
  • 那一年 我廢寢忘食 夜以繼日 也未能走上理想的求學(xué)之路 那一年 我不顧父母的反對(duì),與家人僵持到底 也要堅(jiān)持走自己認(rèn)...
    影落寒溪閱讀 215評(píng)論 0 1
  • 0. 背景Android 6.0 (API level 23)中,將權(quán)限分成了兩類。 一類是Install權(quán)限(稱...
    天空在微笑閱讀 2,337評(píng)論 0 4

友情鏈接更多精彩內(nèi)容