4.RecycleView 嵌套卡顿

RecycleView 嵌套卡顿

https://blog.csdn.net/qq_30983519/article/details/81280274

  1. 共用 RecycledViewPool,holder. innerRecyclerView. setRecycledViewPool (viewPool)
    2. setInitialPrefetchItemCount (int) 来优化嵌套时预加载性能
  2. 设置 recycleview 的子项缓存 rv. setItemViewCacheSize (200);
  3. rv. setHasFixedSize (true);
  4. recyclerview 嵌套 recyclerview 的时候, adapter 全局变量

区别

setInitialPrefetchItemCount

位置:LinearLayoutManager、GridLayoutManager
作用:控制预取 (prefetch) 的数量。
当 RecyclerView 嵌套 RecyclerView(比如外层是列表,内层是横向的 banner/卡片列表)时,外层滑到某个 item,系统会提前帮你把内层 RecyclerView 的子 item 创建/绑定好,避免用户滑动到时才加载导致卡顿。
默认值一般是 2(也就是提前加载 2 个子项)。

👉 优化点:
减少「卡顿感」,让用户滑动到新区域时内容已经准备好。
(主要是滑动流畅度)

setItemViewCacheSize

位置:RecyclerView 本身
作用:设置 RecyclerView 缓存区 (cache) 能保留多少个离屏的 ViewHolder。
默认是 2,也就是说屏幕外的 2 个 item 不会立刻回收,而是放到缓存里,滑回来时可以直接复用,减少重新绑定数据和创建 ViewHolder 的开销。

👉 优化点:
减少频繁的创建和绑定 ViewHolder,在来回滚动时更高效。
(主要是 CPU 开销和回收/复用性能)

对比总结

![[Pasted image 20250909141809.png]]

使用建议
嵌套场景(横向列表 inside 纵向列表):调大 setInitialPrefetchItemCount,一般设成横向列表一屏能展示的数量
单层长列表:可以根据内存情况调大 setItemViewCacheSize,比如设成 10,减少频繁回收。

NestedScrollView 嵌套 recyclerView

NestedScrollview 和 recycler View 嵌套的时候, recyclerview 的缓存机制失效了, 这种情况有没有什么好的解决办法呢?
NestedScrollView 传递给子 View 的测量模式为 UNSPECIFIED,RecyclerView 在 UNSPECIFIED 的测量模式下,会不限制自身的高度,即 RecyclerView 的窗口高度将会变成所有 item 高度累加后加上 paddding 的高度。因此,表现出来就是 item 一次性全部加载完成。
这样做在 RecyclerView 的 item 数量较少的时候可能没什么问题,但是如果 item 数量比较多,随之带来的性能问题就会很严重。

推荐使用 RecyclerView 的多样式布局实现,毕竟 RecyclerView 自带滑动,没必要外层套一个 ScrollerView 或者 NestedScrollView

必须嵌套,解决方案:
懒饭详情页嵌套效果仿写(View/Compose 实现) - 简书
问题本质上其实就是因为高度不确定导致复用失效了,那其实指定 RecyclerView 的高度即可,例如屏幕的高。

  • 在未到达临界高度之前

    • 所有滚动(scroll / fling)都由外层 NestedScrollView 消费
  • 到达临界高度之后

    • 外层不再滚动
    • 剩余的滚动和 fling 交由内部 RecyclerView 处理

![[Pasted image 20250703145905.png]]

[[NestedScrollingParent]]

1
2
3
4
5
6
7
8
9
10
11
/**
* 开始滑动时,子视图会优先回调该方法。父容器可以处理自己的滚动操作,之后将剩余的滚动偏移量传回给子视图。
(可由 NestedScrollingChild2的 dispatchNestedPreScroll 方法触发)
*/
void onNestedPreScroll (@NonNull View target, int dx, int dy, @NonNull int[] consumed, @NestedScrollType int type);

/**
* 子视图处理完剩余的滚动偏移量后,若还有剩余,则将剩余的滚动偏移量再通过该回调传给父容器处理。
(可由 NestedScrollingChild2的 dispatchNestedScroll 方法触发)
*/
void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, @NestedScrollType int type);

实例

scrollViewHeadHeight

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
override fun onNestedPreScroll(target: View, dx: Int, dy: Int, consumed: IntArray, type: Int) {
if (scrollY < scrollViewHeadHeight) {
if (scrollY + dy < scrollViewHeadHeight) {
scrollBy(0, dy)
consumed[1] = dy
} else if (scrollY + dy > scrollViewHeadHeight) {
val scrollViewNeedScrollY = scrollViewHeadHeight - scrollY
scrollBy(0, scrollViewNeedScrollY)
consumed[1] = scrollViewNeedScrollY
}
}
}

override fun fling(velocityY: Int) {
val dy = FlingUtil.getDistanceByVelocity(context, velocityY)
if (scrollY < scrollViewHeadHeight) {
if (scrollY + dy <= scrollViewHeadHeight) {
super.fling(velocityY)
} else if (scrollY + dy > scrollViewHeadHeight) {
val scrollViewNeedScrollY = scrollViewHeadHeight - scrollY
//让NestedScrollView先处理所有的滚动事件
val scrollViewNeedVelocity = FlingUtil.getVelocityByDistance(context, scrollViewNeedScrollY.toDouble())
if (velocityY > 0) {
super.fling(scrollViewNeedVelocity)
} else {
super.fling(-scrollViewNeedVelocity)
}
//把剩余的滚动事件交给RecyclerView处理
val recyclerViewScrollY = dy - scrollViewNeedScrollY
val recyclerViewNeedVelocity = FlingUtil.getVelocityByDistance(context, recyclerViewScrollY)
if (velocityY > 0) {
getChildRecyclerView(this)?.fling(0, recyclerViewNeedVelocity)
} else {
getChildRecyclerView(this)?.fling(0, -recyclerViewNeedVelocity)
}
}
}
}

上面的代码是针对嵌套滚动的情况进行处理的。根据代码的逻辑,可以理解如下:

  1. onNestedPreScroll() 方法用于处理嵌套滚动前的预处理。当嵌套滚动发生时,会调用该方法。参数说明如下:

    • target: 嵌套滚动的目标视图。
    • dx: 水平方向上的滚动距离。
    • dy: 垂直方向上的滚动距离。
    • consumed: 用于传递消耗的滚动距离的数组。
    • type: 滚动事件类型。

    在代码中,首先判断当前滚动的位置 scrollY 是否小于 scrollViewHeadHeight(一个预设的阈值)。如果小于该阈值,表示需要对滚动进行处理。

    • 如果 scrollY + dy 小于 scrollViewHeadHeight,直接进行滚动并消耗全部的垂直滚动距离 dy,将其存入 consumed 数组中。
    • 如果 scrollY + dy 大于 scrollViewHeadHeight,则需要将滚动限制在 scrollViewHeadHeight 的位置,即将滚动距离限制为 scrollViewHeadHeight - scrollY并将此消耗的滚动距离存入 consumed 数组中
  2. fling() 方法用于处理滑动手势的快速滚动。当手指快速滑动屏幕时,会调用该方法。参数说明如下:

    • velocityY: 垂直方向上的滑动速度。

    在代码中,首先判断当前滚动的位置 scrollY 是否小于 scrollViewHeadHeight

    • 如果小于该阈值,表示需要对滚动进行处理。
    • 如果 scrollY + dy 小于等于 scrollViewHeadHeight,直接调用父类的 super.fling(velocityY) 方法进行滚动。
    • 如果 scrollY + dy 大于 scrollViewHeadHeight,则需要将滚动限制在 scrollViewHeadHeight 的位置,即将滚动距离限制为 scrollViewHeadHeight - scrollY,并将此消耗的滚动距离存入 scrollViewNeedScrollY
    • 接下来,根据滚动速度 velocityY 计算出 scrollViewNeedScrollY 所对应的速度 scrollViewNeedVelocity
    • 如果 velocityY 大于 0,表示向上滑动,调用父类的 super.fling(scrollViewNeedVelocity) 方法进行滚动。
    • 如果 velocityY 小于等于 0,表示向下滑动,调用父类的 super.fling(-scrollViewNeedVelocity) 方法进行滚动。
    • 然后,剩余的滚动距离为 dy - scrollViewNeedScrollY,根据剩余距离计算出对应的速度 recyclerViewNeedVelocity
    • 最后,通过 getChildRecyclerView(this)?.fling(0, recyclerViewNeedVelocity) 将剩余的滚动事件交给嵌套的 RecyclerView 处理。

这段代码的目的是在特定的条件下对滚动和快速滑动进行限制和分发,以实现特定的滚动效果和交互行为。具体的实现细节和逻辑可能还需要结合其他代码来全面理解其功能和效果。


4.RecycleView 嵌套卡顿
http://peiniwan.github.io/2025/12/a0bad8cfa7a3.html
作者
六月的雨
发布于
2025年12月16日
许可协议