自定义View中RecyclerView偶现不显示问题求助
问题场景
以ConstraintLayout为根布局的自定义View内,RecyclerView存在间歇性显示异常:有时能正常加载展示数据,有时完全不显示。已在自定义View中手动设置LinearLayoutManager,但问题仍未解决,通过updateProducts方法更新RecyclerView数据。
原XML布局代码
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <View android:layout_width="@dimen/generic_dimen_0dp" android:layout_height="@dimen/generic_dimen_1dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" android:background="@color/tabs_border_gray"/> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/recentlyViewedTitle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="@dimen/custom_margin_20dp" android:layout_marginTop="@dimen/generic_dimen_35dp" android:fontFamily="@font/es_web_med" android:textSize="16sp" android:padding="@dimen/custom_margin_5dp" android:text="Frequently Bought Together" android:textColor="@color/black" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toTopOf="@+id/productsRecycler"/> <androidx.recyclerview.widget.RecyclerView android:id="@+id/productsRecycler" android:layout_width="match_parent" android:layout_height="wrap_content" android:clipToPadding="false" android:orientation="horizontal" android:paddingHorizontal="@dimen/custom_margin_20dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" tools:itemCount="4" tools:listitem="@layout/item_frequently_bought_together" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
原自定义View Kotlin代码
class FrequentlyBoughtTogetherComponent @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = ZERO, ) : ConstraintLayout(context, attrs, defStyleAttr) { private val binding = FrequentlyBoughtTogetherComponentBinding.inflate(LayoutInflater.from(context), this, false) private var productsAdapter: FrequentlyBoughtTogetherAdapter? = null init { setView() addView(binding.root) } private fun setView() = binding.run { productsAdapter = FrequentlyBoughtTogetherAdapter() productsRecycler.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) productsRecycler.adapter = productsAdapter } fun updateProducts(products: List<RecommendationProduct>) { productsAdapter?.submitList(products) } }
解决办法
1. 修复自定义View布局加载逻辑
当前代码中,Binding inflate时已指定父布局为this,却又手动调用addView(binding.root),导致布局嵌套重复,干扰测量流程。修改方式:
将inflate的第三个参数设为true,让系统自动将根布局挂载到自定义View,移除多余的addView调用。
修改后的Kotlin代码:
class FrequentlyBoughtTogetherComponent @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, ) : ConstraintLayout(context, attrs, defStyleAttr) { private val binding: FrequentlyBoughtTogetherComponentBinding private var productsAdapter: FrequentlyBoughtTogetherAdapter? = null init { binding = FrequentlyBoughtTogetherComponentBinding.inflate(LayoutInflater.from(context), this, true) setView() } private fun setView() = binding.run { productsAdapter = FrequentlyBoughtTogetherAdapter() productsRecycler.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) productsRecycler.adapter = productsAdapter } fun updateProducts(products: List<RecommendationProduct>) { productsAdapter?.submitList(products) } }
2. 完善RecyclerView的约束关系
原XML中RecyclerView仅约束了左右和底部,未明确约束顶部与标题的关联,导致ConstraintLayout无法正确计算RecyclerView的高度范围。给RecyclerView添加顶部约束:
修改后的RecyclerView布局代码:
<androidx.recyclerview.widget.RecyclerView android:id="@+id/productsRecycler" android:layout_width="match_parent" android:layout_height="wrap_content" android:clipToPadding="false" android:orientation="horizontal" android:paddingHorizontal="@dimen/custom_margin_20dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@+id/recentlyViewedTitle" app:layout_constraintBottom_toBottomOf="parent" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" tools:itemCount="4" tools:listitem="@layout/item_frequently_bought_together" />
3. 验证ListAdapter的DiffUtil实现
如果使用ListAdapter,确保DiffUtil.ItemCallback的areItemsTheSame和areContentsTheSame方法逻辑正确,否则submitList可能无法触发数据更新。若暂时无法排查DiffUtil问题,可在updateProducts中临时添加notifyDataSetChanged()验证:
fun updateProducts(products: List<RecommendationProduct>) { productsAdapter?.submitList(products) productsAdapter?.notifyDataSetChanged() }
注意:notifyDataSetChanged()是全局刷新,性能较差,仅用于排查问题,确认后仍需修复DiffUtil逻辑。
4. 强制触发RecyclerView重绘
在提交数据后,通过post方法触发RecyclerView重新测量布局,确保数据更新后界面能及时刷新:
fun updateProducts(products: List<RecommendationProduct>) { productsAdapter?.submitList(products) binding.productsRecycler.post { binding.productsRecycler.requestLayout() } }
内容的提问来源于stack exchange,提问作者Alexis Rivas

