首页 > 解决方案 > 当滚动太远时,如何防止 RecyclerView 为每个中间子级运行 onBindViewHolder

问题描述

我有一个RecyclerView作为日历,每个视图都是一天。

(占据屏幕的全宽和全高)

     .----------------.   .----------------.   .----------------.   .----------------.   .----------------.   .----------------.   .----------------.
    | .--------------. | | .--------------. | | .--------------. | | .--------------. | | .--------------. | | .--------------. | | .--------------. |
    | |              | | | |              | | | |              | | | |              | | | |              | | | |              | | | |              | |
    | |              | | | |              | | | |              | | | |              | | | |              | | | |              | | | |              | |
    | |              | | | |              | | | |              | | | |              | | | |              | | | |              | | | |              | |
    | |              | | | |              | | | |              | | | |              | | | |              | | | |              | | | |              | |
    | |              | | | |              | | | |              | | | |              | | | |              | | | |              | | | |              | |
    | |     (1)      | | | |     (2)      | | | |     (3)      | | | |     (4)      | | | |     (5)      | | | |    (...)     | | | |     (n)      | |
    | |              | | | |              | | | |              | | | |              | | | |              | | | |              | | | |              | |
    | '--------------' | | '--------------' | | '--------------' | | '--------------' | | '--------------' | | '--------------' | | '--------------' |
     '----------------'   '----------------'   '----------------'   '----------------'   '----------------'   '----------------'   '----------------'

问题:

问题是,当用户试图滚动到未来非常遥远的日期时,即向前移动 40 天,onBindViewHolder会为中间的所有 39 个孩子运行,这会浪费资源,因为滚动太快了,甚至没有人知道在那卷轴中每一天都包含什么。

因此,最后一天需要一些时间才能显示内容,因为主线程正忙于绘制所有中间天,除非用户手动滚动到它们,否则这些天将永远不可见。

需要什么:

我怎样才能防止 RecyclerView疯狂地加载所有这些中间的日子,而此时我所需要的只是用户结束的那一天。

     .----------------.   .----------------.   .----------------.   .----------------.   .----------------.   .----------------.   .----------------.
    | .--------------. | | .--------------. | | .--------------. | | .--------------. | | .--------------. | | .--------------. | | .--------------. |
    | |              | | | |              | | | |              | | | |              | | | |              | | | |              | | | |              | |
    | |              | | | |              | | | |              | | | |              | | | |              | | | |              | | | |              | |
    | |              | | | |              | | | |              | | | |              | | | |              | | | |              | | | |              | |
    | |              | | | |              | | | |              | | | |              | | | |              | | | |              | | | |              | |
    | |              | | | |              | | | |              | | | |              | | | |              | | | |              | | | |              | |
    | |     (1)      | | | |     (2)      | | | |     (3)      | | | |     (4)      | | | |     (5)      | | | |    (...)     | | | |     (n)      | |
    | |    LOAD      | | | |     DONT     | | | |    DONT      | | | |    DONT      | | | |     DONT     | | | |     DONT     | | | |     LOAD     | |
    | '--------------' | | '--------------' | | '--------------' | | '--------------' | | '--------------' | | '--------------' | | '--------------' |
     '----------------'   '----------------'   '----------------'   '----------------'   '----------------'   '----------------'   '----------------'

(请不要告诉我使用scrollToPositionsmoothScrollToPosition因为删除动画不是一个选项)

谢谢

标签: androidandroid-recyclerviewrecyclerview-layout

解决方案


你可以修改你的Adapter类来检查平滑滚动的状态:

@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
    if(recyclerView.getLayoutManager().isSmoothScrolling()){
        // make viewholder into a "blank" here
        return;
    }
    // regular binding
}

请注意,它有点粗略 - 应该ViewHolder在滚动期间保持空白,否则它将显示无效天数。

这就像它得到的一样优雅(和简单),因为SmoothScroller它实际上非常原始 - 它只是一直滚动直到找到目标视图,所以没有回调来知道何时已经布置了具有目标位置的视口。

绑定滚动“目标”时,您需要重新验证可见持有人。

ViewHolder只有当您的 s 具有不变的统一大小并且您向适配器提供 smoothScroll 目标时,更完美的解决方案才能工作:

@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
    if(recyclerView.getLayoutManager().isSmoothScrolling()){
        // scrollTarget is current smoothScroll target position
        int diff = Math.abs(position - scrollTarget);
        // viewHolderHeight is taken/cached from any viewHolder that was laid out previously
        if(diff * viewHolderHeight > recyclerView.getHeight()){
            // make viewholder into a "blank" here
            return;
        }
        // still smoothscrolling, but viewholder will be visible when scroll ends
    }
    // regular binding
}

推荐阅读