StaggeredGridLayoutManager滑动到顶部Item跳动

865 阅读2分钟

Bug 复现

在滑动RecyclerView到一定位置,不显示RecyclerView的顶部Item时,调用notifyDataChanged()刷新RecyclerView 然后再滑动到顶部Item就会跳动闪烁.

滑动到顶部Item跳动原因

StaggeredGridLayoutManager中的LazySpanLookup保存了Item中的跨度,RecyclerView从底部滑动到顶部时,因为StaggeredGridLayoutMananger重写了onScrollStateChanged(),清空了LazySpanLookup中保存的跨度.调用链如下:onScrollStateChanged()→checkForGaps()→mLazySpanLookup.clear()清空保存的宽度Span后下次会重新布局,导致滑动到顶部的时候会有一个item跳动的效果.

static class LazySpanLookup {

    private static final int MIN_SIZE = 10;
    //保存Item的跨度
    int[] mData;
    List<FullSpanItem> mFullSpanItems;
}

调用StaggeredGridLayoutMananger.setGapStrategy(GAP_HANDLING_NONE)设置Item之间的间隙策略.当Item存在间隙时不自动重新排列布局.就不会导致顶部的Item跳动.这个方法如果不调用notifyDataChanged()更新界面就不会有问题.但是如果在底部调用notifyDataChanged()就会会引起顶部Item存在空白.

网上找到的方法都没有解决我的问题
1.RecyclerView高级进阶之优雅地解决瀑布流的两个神坑 - 掘金 (juejin.cn)
2.RecyclerView瀑布流空白、重新排序原因及解决办法 - 简书 (jianshu.com)

解决方法

//设置LayoutManagwer的空隙策略为不自动填充Item空白
layoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE);

然后重写StaggeredGridLayoutManager的onItemUpadted()

/**
 * 解决调用Adapter.notifyRangeChanged() Item显示错乱的问题
 */
public class SensorInfoLayoutManager extends StaggeredGridLayoutManager {

    public SensorInfoLayoutManager(int spanCount, int orientation) {
        super(spanCount, orientation);
    }

    @Override
    public void onItemsUpdated(RecyclerView recyclerView, int positionStart,
                               int itemCount, Object payload) {
        //空实现
        //为了解决RecyclerView刷新界面Item顺序错乱
    }
}

因为我的RecyclerView 的Item大小都是固定不变的,只需要更新Item的内容,不需要更新大小.这个方法能解决我的问题.
注意:如果刷新界面时,Item大小会改变,如果采用这个方法可能导致其他问题(这我没测试过)