使用RecyclerView + ViewPager 的兩個大坑!

問題

在RecyclerView中使用ViewPager時,會出現兩個詭異的bug:

  1. RecyclerView滾動上去,直至ViewPager看不見,再滾動下來,ViewPager下一次切換沒有動畫
  2. 當ViewPage滾動到一半的時候,RecyclerView滾動上去,再滾動下來,ViewPager會卡在一半

這兩個bug只能用兩個字形容:大坑!

問題1:原因

ViewPager裏有一個私有變量mFirstLayout,它是表示是不是第一次顯示佈局,如果是true,則使用無動畫的方式顯示當前item,如果是false,則使用動畫方式顯示當前item。

看源碼

void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
    ...

    if (mFirstLayout) {
        // We don't have any idea how big we are yet and shouldn't have any pages either.
        // Just set things up and let the pending layout handle things.
        mCurItem = item;
        if (dispatchSelected) {
            dispatchOnPageSelected(item);
        }
        requestLayout();
    } else {
        populate(item);
        scrollToItem(item, smoothScroll, velocity, dispatchSelected);

    ...
}

當ViewPager滾動上去後,因爲RecyclerView的回收機制,ViewPager會走onDetachFromWindow,當再次滾動下來時,ViewPager會走onAttachedToWindow,而問題就出在onAttachToWindow。

看源碼:

@Override
protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    mFirstLayout = true;
}

原來如此,在onAttachedToWindow中,mFirstLayout被重置爲true,所以下一次滾動就沒有動畫。

問題1:解決方法

重寫onAttachedToWindow方法,把mFirstLayout再重置成false,因爲mFirstLayout是private變量,我們不能直接訪問,所以只能反射了。

@Override
protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    try {
        Field mFirstLayout = ViewPager.class.getDeclaredField("mFirstLayout");
        mFirstLayout.setAccessible(true);
        mFirstLayout.set(this, false);
        getAdapter().notifyDataSetChanged();
        setCurrentItem(getCurrentItem());
    } catch (Exception e) {
        e.printStackTrace();
    }
}

問題2:原因

直接來看ViewPager的onDetachFromWindow方法

@Override
protected void onDetachedFromWindow() {
    removeCallbacks(mEndScrollRunnable);
    // To be on the safe side, abort the scroller
    if ((mScroller != null) && !mScroller.isFinished()) {
        mScroller.abortAnimation();
    }
    super.onDetachedFromWindow();
}

尼瑪,直接把動畫強行停掉了。

問題2:解決方法

想來想去,沒什麼好辦法,只能想辦法保護了

@Override
protected void onDetachedFromWindow() {
    if (hasActivityDestroy) {
        super.onDetachedFromWindow();
    }
}

public void setHasDestroy(boolean hasDestroy) {
    hasActivityDestroy= hasDestroy;
}

當activitydestroy的時候,給自定義ViewPager一個標誌位hasActivityDestroy,只有hasActivityDestroy爲true的時候,才調用父類的super.onDetachedFromWindow();

總結

這兩個bug看了我好久啊,真是個大坑。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章