如何让NestedScrollView内的RecyclerView保持正常视图复用特性?
解决RecyclerView在NestedScrollView中失去项复用的问题
你的问题核心在于RecyclerView的layout_height设为wrap_content时,放在NestedScrollView中会强制测量所有子项的总高度,导致一次性创建所有ViewHolder,完全失去复用特性,进而出现加载等待的卡顿。下面提供两种解决方案:
方案一:保留NestedScrollView,修复RecyclerView的高度约束
只需要修改布局中RecyclerView的属性,让它填充父布局的可用空间,而非自适应内容高度:
修改fragment_translate_from.xml中的RecyclerView部分:
<androidx.recyclerview.widget.RecyclerView android:id="@+id/all_langs_recycler_view" android:layout_width="0dp" android:layout_height="0dp" <!-- 从wrap_content改为0dp --> android:nestedScrollingEnabled="false" android:orientation="vertical" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tv_all_languages" app:layout_constraintBottom_toBottomOf="parent" /> <!-- 添加底部约束 -->
原理
当RecyclerView高度设为0dp并通过约束填充可用空间时,它会基于当前可见区域的高度来创建ViewHolder,而非一次性加载所有项,这样就保留了复用特性,解决卡顿问题。
方案二:移除NestedScrollView,使用RecyclerView Header替代单独标题(更优)
如果你的布局中只有标题+RecyclerView需要滚动,完全不需要NestedScrollView,直接把标题作为RecyclerView的Header,性能更优:
步骤1:创建Header Adapter
class LanguageHeaderAdapter(private val title: String) : RecyclerView.Adapter<LanguageHeaderAdapter.HeaderViewHolder>() { class HeaderViewHolder(private val binding: LanguageHeaderBinding) : RecyclerView.ViewHolder(binding.root) { fun bind(title: String) { binding.tvTitle.text = title } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HeaderViewHolder { val binding = LanguageHeaderBinding.inflate(LayoutInflater.from(parent.context), parent, false) return HeaderViewHolder(binding) } override fun onBindViewHolder(holder: HeaderViewHolder, position: Int) { holder.bind(title) } override fun getItemCount(): Int = 1 }
步骤2:修改Fragment中的Adapter初始化
在setAllLanguagesRecyclerView方法中,用ConcatAdapter合并Header和语言列表Adapter:
private fun setAllLanguagesRecyclerView() { lifecycleScope.launch(Dispatchers.IO) { languagesList = viewModel.getAllLanguagesName().toMutableList() val layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) val position = viewModel.getAllSelectedPosition() val adapter = AllLanguagesAdapter { allLangModel -> viewModel.insertRecentLanguages(allLangModel) sendBackValueToPreviousBackStack(allLangModel.langCode) } // 添加Header val headerAdapter = LanguageHeaderAdapter(getString(R.string.all_languages_str)) val concatAdapter = ConcatAdapter(headerAdapter, adapter) binding.allLangsRecyclerView.layoutManager = layoutManager adapter.submitList(languagesList) withContext(Dispatchers.Main) { binding.allLangsRecyclerView.adapter = concatAdapter // 使用合并后的Adapter setBackgroundToLanguagesRecyclerViewItem( recyclerView = binding.allLangsRecyclerView, position = position + 1 // 注意Header占了一个位置,position需要+1 ) setAdiveryNativeAd(adapter) setSearch(adapter) } } }
步骤3:简化布局
移除NestedScrollView和内层ConstraintLayout,直接让RecyclerView填充父布局:
<androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/views_layout_translate_from" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/all_langs_recycler_view" android:layout_width="0dp" android:layout_height="0dp" android:orientation="vertical" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
原理
RecyclerView本身就具备完整的滚动和复用机制,添加Header后依然会正常复用列表项,同时避免了NestedScrollView带来的额外测量开销,是性能最优的方案。
内容的提问来源于stack exchange,提问作者StackOverflower
相关产品推荐
相关产品推荐

