1. BottomSheetDialogFragment高度控制全攻略BottomSheetDialogFragment的高度问题绝对是开发中最常遇到的坑点之一。记得我第一次用的时候弹窗要么铺满全屏要么缩成一团完全不受控制。经过多次实战我总结出几种高度控制方案帮你避开这些坑。1.1 固定高度展开的终极方案如果你需要弹窗固定展开比如商品详情页的底部弹窗千万别用STATE_EXPANDED就完事了。正确的做法是在onCreateView里这样写override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { dialog?.let { (it as? BottomSheetDialog)?.behavior?.apply { skipCollapsed true // 跳过中间状态 state BottomSheetBehavior.STATE_EXPANDED // 强制展开 isDraggable false // 禁止拖拽 } } // 正常inflate布局... }这里有个隐藏坑点skipCollapsed必须在state之前设置否则动画会变得很奇怪。我当初就因为顺序问题调试了半天。1.2 动态计算最大高度的正确姿势当需要限制弹窗最大高度时比如不超过屏幕60%网上很多方案都是在onCreateDialog里设置这其实是个误区。最佳实践是在onCreateView阶段计算fun Window.getScreenHeight(): Int { return if (Build.VERSION.SDK_INT Build.VERSION_CODES.R) { windowManager.currentWindowMetrics.bounds.height() } else { Point().apply { windowManager.defaultDisplay.getSize(this) }.y } } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val maxHeight (requireActivity().window.getScreenHeight() * 0.6).toInt() // 方案1直接设置根布局高度适合简单布局 binding.root.layoutParams LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, maxHeight ) // 方案2通过Behavior设置保留拖拽功能时用 (dialog as? BottomSheetDialog)?.behavior?.maxHeight maxHeight return binding.root }实测发现XML中设置android:maxHeight在某些机型上会失效代码动态设置最可靠。如果是复杂布局建议用ConstraintLayout的app:layout_constraintHeight_max属性。2. 滚动冲突的完美解决方案2.1 RecyclerView vs NestedScrollView选型当弹窗内容需要滚动时90%的滚动冲突都源于选错容器。我的踩坑经验是简单文本用NestedScrollView包裹复杂列表必须用RecyclerView图文混排即使用单个Item也要选RecyclerView关键配置androidx.recyclerview.widget.RecyclerView android:layout_widthmatch_parent android:layout_heightwrap_content android:nestedScrollingEnabledtrue !-- 必须开启 -- android:overScrollModenever !-- 禁用边缘效果 -- app:layoutManagerLinearLayoutManager /2.2 键盘弹出时的布局调整含有EditText时这个坑我踩了三次才填平不要设置沉浸式状态栏WindowCompat.setDecorFitsSystemWindows必须处理键盘遮挡override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val dialog super.onCreateDialog(savedInstanceState) dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) return dialog }对于Android 11还需要额外配置style nameBottomSheetDialogTheme parentTheme.MaterialComponents.Light.BottomSheetDialog item nameandroid:windowLayoutInDisplayCutoutModeshortEdges/item item nameenableEdgeToEdgetrue/item /style3. 高级功能实现技巧3.1 自定义Toast父容器破解方案在弹窗里显示Toast时默认会挂在DecorView上位置可能不对。正确做法是重写查找父容器的方法fun findToastParent(): ViewGroup? { return view?.parent?.let { parent - (parent as? ViewGroup)?.parent as? CoordinatorLayout } } // 使用示例 Toast(requireContext()).apply { setView(toastView) duration Toast.LENGTH_SHORT // 关键设置自定义父容器 (this as? Toast)?.let { val toast it toast.view?.parent?.let { (it as? ViewManager)?.removeView(toast.view) } findToastParent()?.addView(toast.view) } show() }3.2 拦截dismiss事件的正确方式系统自带的setOnDismissListener有个致命缺陷回调时UI已经消失。我们需要自定义Dialogclass InterceptDismissDialog( context: Context, theme: Int ) : BottomSheetDialog(context, theme) { var beforeDismiss: (() - Unit)? null override fun dismiss() { beforeDismiss?.invoke() super.dismiss() // 必须放在最后 } } // 在Fragment中使用 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { return InterceptDismissDialog(requireContext(), R.style.Theme_App_BottomSheet).apply { beforeDismiss { // 在这里执行保存数据等操作 saveFormData() } } }4. 性能优化与细节处理4.1 避免布局闪烁的技巧弹窗出现时的闪烁问题很常见关键是要在onCreateDialog里完成所有初始化override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val dialog super.onCreateDialog(savedInstanceState) // 提前设置Behavior参数 dialog.setOnShowListener { dialog.findViewByIdView(com.google.android.material.R.id.design_bottom_sheet)?.let { sheet - BottomSheetBehavior.from(sheet).apply { skipCollapsed true state BottomSheetBehavior.STATE_EXPANDED // 其他参数... } } } return dialog }4.2 内存泄漏预防方案BottomSheetDialogFragment容易引发内存泄漏主要注意三点在onDestroyView里释放绑定override fun onDestroyView() { binding.unbind() _binding null super.onDestroyView() }避免在回调中直接引用Fragment// 错误做法 beforeDismiss { saveData() } // 正确做法 beforeDismiss { [weak this] in this?.saveData() }使用ViewLifecycleOwner管理LiveData观察viewLifecycleOwner.lifecycleScope.launch { viewModel.data.collect { updateUI(it) } }最后分享一个实用技巧给BottomSheet添加圆角只需在style中配置style nameBottomSheet parentWidget.MaterialComponents.BottomSheet.Modal item nameshapeAppearanceOverlaystyle/ShapeAppearanceBottomSheet/item /style style nameShapeAppearanceBottomSheet parent item namecornerFamilyrounded/item item namecornerSizeTopLeft16dp/item item namecornerSizeTopRight16dp/item /style