当滚动距离太远时,如何防止RecyclerView在每个中间子对象上运行onBindViewHolder

时间:2018-08-02 20:19:06

标签: android android-recyclerview recycler-adapter recyclerview-layout

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

(占据屏幕的整个宽度和高度)

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

问题:

问题是,当用户尝试滚动到将来很远的日期(即,向前移动40天)时,onBindViewHolder将为中间的所有39个孩子运行,这会浪费资源,因为滚动是如此之快,所以甚至没人知道滚动期间的每一天。

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

需要什么:

我该如何防止RecyclerView 发疯在几天之内加载所有这些,而此时我所需要的只是用户最终要离开的那一天。

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

(请不要告诉我使用scrollToPosition而不是smoothScrollToPosition,因为不能删除动画)

谢谢

2 个答案:

答案 0 :(得分:2)

您可以修改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的大小不变且您向适配器提供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
}

答案 1 :(得分:0)

花了将近2天的时间对其进行调试之后,我想到了一些解决方法。

问题最终是scrollBy方法,我使用该方法可以更好地控制滚动到的位置。

即使滚动没有通过任何中间的子级,也调用了onBindViewHolder。这太奇怪了,对我来说根本没有意义,(这是RecyclerView的错误吗? ?)

我最终使用了scrollToPosition,它只为最终的孩子调用onBindViewHolder-因此可以解决我遇到的所有问题。

是的,我牺牲了滚动精度,但至少我只绑定了我需要的内容-最后!