Android BottomSheetDialogFragment内ChipGroup二次打开崩溃问题修复
问题:BottomSheetDialogFragment二次打开时ChipGroup空指针崩溃
在Android应用的BottomSheetDialogFragment中包含ChipGroup组件,首次打开对话框时能正常选中目标Chip,但关闭后再次打开会抛出NullPointerException,错误指向代码行mealChipGroup.findViewById<Chip>(3).isChecked = true。需要排查并修复该问题。
问题代码
class MenuFragment : BottomSheetDialogFragment() { // Binding private var _binding: FragmentMenuBinding? = null private val binding get() = _binding!! override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { _binding = FragmentMenuBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // InitView binding.apply { setupChip(fillChipData(), mealChipGroup) mealChipGroup.findViewById<Chip>(3).isChecked = true } } private fun fillChipData(): MutableList<String> { return mutableListOf( "Item1", "Item2", "Item3", "Item4", "Item5" ) } private fun setupChip(list: MutableList<String>, view: ChipGroup) { list.forEach { val chip = Chip(requireContext()) chip.text = it view.addView(chip) } } override fun onDestroy() { super.onDestroy() _binding = null } }
错误信息
java.lang.NullPointerException: Attempt to invoke virtual method 'void com.google.android.material.chip.Chip.setChecked(boolean)' on a null object reference at myapp.MenuFragment.onViewCreated(MenuFragment.kt:58) at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:3128) at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:552) at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261) at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1899) at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1823) at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1760) at androidx.fragment.app.FragmentManager$5.run(FragmentManager.java:547) at android.os.Handler.handleCallback(Handler.java:751) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6121) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)
问题原因
- 系统自动分配ID不可靠:首次打开时,系统为动态添加的Chip自动分配ID,碰巧存在ID为3的Chip;但Fragment销毁重建时,系统分配的ID会随机变化,二次打开时
findViewById(3)无法找到对应Chip,返回null触发空指针。 - 重复添加Chip:每次打开Fragment都会调用
setupChip,但未清空ChipGroup原有内容,多次打开后ChipGroup会堆积重复Chip,进一步导致ID匹配逻辑混乱。
修复方案
方案1:给Chip设置固定唯一ID
手动给每个Chip分配固定ID,彻底摆脱对系统自动生成ID的依赖:
private fun setupChip(list: MutableList<String>, view: ChipGroup) { // 先清空ChipGroup,避免重复添加 view.removeAllViews() list.forEachIndexed { index, text -> val chip = Chip(requireContext()) chip.text = text // 用固定前缀+索引设置唯一ID,避免和其他控件ID冲突 chip.id = 1000 + index view.addView(chip) } }
修改选中逻辑,使用对应的固定ID,并添加空安全调用:
// 要选中第三个Item(索引2),对应ID为1000+2=1002 mealChipGroup.findViewById<Chip>(1002)?.isChecked = true
方案2:通过子View索引获取Chip
由于Chip是按顺序添加的,可直接通过索引定位目标Chip,无需依赖ID:
binding.apply { setupChip(fillChipData(), mealChipGroup) // 第三个Chip的索引为2(从0开始计数),先判断子View数量避免越界 if (mealChipGroup.childCount > 2) { val targetChip = mealChipGroup.getChildAt(2) as Chip targetChip.isChecked = true } }
同样需要在setupChip开头添加view.removeAllViews()避免重复添加。
额外注意
- 动态添加View前务必清空容器,避免重复View堆积;
- 动态创建的View不要依赖系统自动分配的ID,尽量手动设置固定唯一ID;
- 优先使用Kotlin空安全调用
?.,即使找不到View也不会崩溃,可配合?:做兜底处理。
内容的提问来源于stack exchange,提问作者Dr.KeyOk
相关产品推荐
相关产品推荐

