使用ConstraintLayout实现BottomSheetDialog时无内容显示问题咨询
问题复现
需求为实现承载可滚动列表的模态底部弹窗,相关实现代码如下:
布局文件 dialog_bottom_example.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/white" xmlns:app="http://schemas.android.com/apk/res-auto"> <LinearLayout android:id="@+id/container" android:layout_width="0dp" android:layout_height="0dp" android:orientation="vertical" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
自定义 BottomSheetDialogFragment 实现 ExampleDialogBottom
class ExampleDialogBottom: BottomSheetDialogFragment() { private lateinit var binding: DialogExampleBinding private var skusItems = mutableListOf<SkuItem>(); companion object { internal const val TAG = "ExampleDialogBottom" @JvmStatic fun newInstance(nonEligibleErxItem: NonEligibleErxItem) = NonEligibleBottomDialog } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { binding = DialogExampleBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) configureSkuItems() } private fun configureSkuItems() { if (!skuItems.isNullOrEmpty()) { val inflater = LayoutInflater.from(context) for (sku in skuItems) { val skuItemView = SingleSkuItemViewBinding.inflate(inflater, null, false) skuItemView.skuImage.setImageWithVisibility(sku.imageUrl) skuItemView.skuTitle.setTextWithVisibility(sku.name) binding.container.addView(skuItemView.root) } binding.container.visibility = View.VISIBLE } else { binding.container.visibility = View.GONE } } }
Activity 触发逻辑
button.setOnClickListener { ExampleDialogBottom.newInstance().show(getSupportFragmentManager(), ExampleDialogBottom.TAG); }
问题现象
点击按钮仅弹出透明弹窗,无任何内容显示。将LinearLayout的match_constraint(0dp)改为match_parent或者wrap_content后功能运行正常。
问题产生原因
- ConstraintLayout中宽高设为0dp(即match_constraint)的子View,尺寸完全依赖父布局ConstraintLayout完成测量后才能确定最终大小。而BottomSheetDialogFragment的根布局测量流程有特殊性:在动态向LinearLayout添加子View的阶段,父ConstraintLayout还未完成最终的测量计算,导致LinearLayout的测量宽高结果为0,即便添加了子View也无法显示。
- 当把LinearLayout的宽高改为
match_parent或wrap_content时,子View不需要依赖父布局的约束计算就能自行测量出尺寸,所以添加的子View可以正常渲染。 - 代码还存在隐藏问题:
newInstance方法接收了参数但没有将商品列表赋值给类属性skusItems,如果未补全赋值逻辑,skuItems会一直是空列表,也会导致容器隐藏无内容。
正确解决方案
可按需选择以下任意方案:
方案1:保留ConstraintLayout约束写法,补充默认尺寸约束
给LinearLayout加上约束默认宽高属性,告诉ConstraintLayout在约束未计算完成时先按规则适配尺寸:
<LinearLayout android:id="@+id/container" android:layout_width="0dp" android:layout_height="0dp" android:orientation="vertical" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" <!-- 新增以下两行 --> app:layout_constraintWidth_default="match" app:layout_constraintHeight_default="wrap" />
如果列表过长需要限制弹窗最大高度避免顶满屏幕,还可以给根ConstraintLayout加上最大高度限制:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:maxHeight="350dp" <!-- 按需调整最大高度 --> android:background="@color/white" xmlns:app="http://schemas.android.com/apk/res-auto">
方案2:替换更适配的布局结构(推荐)
因为是可滚动列表,直接用NestedScrollView包裹LinearLayout作为容器,不需要嵌套ConstraintLayout,性能更好也不会出现测量问题:
<?xml version="1.0" encoding="utf-8"?> <androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white" android:maxHeight="350dp"> <LinearLayout android:id="@+id/container" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"/> </androidx.core.widget.NestedScrollView>
补充修复隐藏问题
补全newInstance的参数赋值逻辑,确保sku列表能正常传入:
@JvmStatic fun newInstance(nonEligibleErxItem: NonEligibleErxItem) = ExampleDialogBottom().apply { // 假设nonEligibleErxItem里有sku列表属性,按需对应赋值 skusItems = nonEligibleErxItem.skuList.toMutableList() }
内容的提问来源于stack exchange,提问作者pandey_shubham
相关产品推荐
相关产品推荐

