如何在不使用NestedScrollView的情况下滚动含RecyclerView的页面?
问题
布局中RecyclerView与其他元素嵌套在NestedScrollView内,当RecyclerView条目过多时界面卡顿数秒,移除NestedScrollView后恢复正常,但需要实现整页滚动效果,如何在不使用NestedScrollView的前提下达成需求?
原布局代码:
<androidx.core.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" android:orientation="vertical" android:overScrollMode="never" android:scrollbars="none"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <!-- Other elements --> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerTask" android:layout_width="match_parent" android:layout_height="wrap_content" android:clipToPadding="false" android:orientation="vertical" android:overScrollMode="never" android:paddingTop="4dp" android:paddingBottom="4dp" android:scrollbars="none" tools:listitem="@layout/item_task" /> </LinearLayout> </androidx.core.widget.NestedScrollView>
RecyclerView初始化代码:
binding.recyclerTask.apply { layoutManager = LinearLayoutManager(activity) itemAnimator = null adapter = adapterTasks }
解决方案
卡顿根源是NestedScrollView会强制RecyclerView使用wrap_content高度,导致RecyclerView一次性创建所有条目的ViewHolder,完全失去复用机制,条目过多时必然卡顿。以下是两种可行的替代方案:
方案一:给RecyclerView添加Header视图
将原来的"Other elements"作为RecyclerView的Header,让整个页面变为单一RecyclerView,既实现整页滚动,又保留RecyclerView的复用特性。
- 修改原Adapter,支持Header类型:
class TaskAdapter(private val headerView: View, private val taskList: List<Task>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { private val TYPE_HEADER = 0 private val TYPE_ITEM = 1 override fun getItemViewType(position: Int): Int { return if (position == 0) TYPE_HEADER else TYPE_ITEM } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return if (viewType == TYPE_HEADER) { HeaderViewHolder(headerView) } else { val view = LayoutInflater.from(parent.context).inflate(R.layout.item_task, parent, false) TaskViewHolder(view) } } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { if (holder is TaskViewHolder) { val task = taskList[position - 1] // 绑定任务数据逻辑 } } override fun getItemCount(): Int { return taskList.size + 1 // 加上Header的1个条目 } class HeaderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) class TaskViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { // 绑定任务条目的视图控件 } }
- 初始化RecyclerView时传入Header视图:
// 加载Header布局 val headerView = LayoutInflater.from(activity).inflate(R.layout.layout_other_elements, binding.recyclerTask, false) // 创建Adapter val adapter = TaskAdapter(headerView, taskList) // 设置RecyclerView binding.recyclerTask.apply { layoutManager = LinearLayoutManager(activity) itemAnimator = null adapter = adapter }
方案二:使用ConcatAdapter组合多个Adapter(推荐)
如果不想修改原TaskAdapter,可以用AndroidX提供的ConcatAdapter,将Header对应的独立Adapter和原TaskAdapter合并,实现多部分内容的连续滚动。
- 创建Header对应的Adapter:
class HeaderAdapter(private val headerView: View) : RecyclerView.Adapter<HeaderAdapter.HeaderViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HeaderViewHolder { return HeaderViewHolder(headerView) } override fun onBindViewHolder(holder: HeaderViewHolder, position: Int) { // 绑定Header数据(如果需要) } override fun getItemCount(): Int { return 1 } class HeaderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) }
- 用ConcatAdapter合并两个Adapter:
// 加载Header布局 val headerView = LayoutInflater.from(activity).inflate(R.layout.layout_other_elements, binding.recyclerTask, false) val headerAdapter = HeaderAdapter(headerView) // 合并Adapter val concatAdapter = ConcatAdapter(headerAdapter, adapterTasks) // 设置RecyclerView binding.recyclerTask.apply { layoutManager = LinearLayoutManager(activity) itemAnimator = null adapter = concatAdapter }
注意事项
- 移除原布局中的NestedScrollView和外层LinearLayout,直接将RecyclerView设为页面根布局的子视图,宽度
match_parent,高度match_parent。 - 两种方案都能让RecyclerView正常复用ViewHolder,避免一次性加载所有条目,从根本解决卡顿问题。
内容的提问来源于stack exchange,提问作者Victor
相关产品推荐
相关产品推荐

