使用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看了我好久啊,真是个大坑。。。

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