实现带复选框的Android RecyclerView时遇空列表索引越界错误
问题排查与修复
问题出在这两个关键地方:
Adapter的条目数计算完全错误
你在getItemCount()里返回的是floors.toString().length——这是把列表转成字符串后的字符长度,比如空列表转成[],长度是2,RecyclerView就会以为有2个条目要加载,但实际列表是空的,访问索引0自然就抛出越界异常了。正确的应该返回列表实际的元素数量floors.size。更新数据后没告诉RecyclerView要刷新
在getAllFloors()里你只是把新列表赋值给了floors,但RecyclerView根本不知道数据变了,还是按旧的条目数来渲染,这也会导致数据不同步的问题。必须在赋值后调用notifyDataSetChanged()通知刷新。
另外,Dialog Fragment里的floorList还有个小坑:每次totalFloors更新时,你都会往同一个列表里加元素,多次触发的话列表会重复。最好每次处理新数据时重新创建列表,或者先清空再添加。
修复后的代码
1. 修正Adapter
class FloorProfileDialogAdapter() : RecyclerView.Adapter<FloorProfileDialogAdapter.MyViewHolder>() { var floors = emptyList<String>() inner class MyViewHolder(val binding: ScheduleFloorDialogItemBinding) : RecyclerView.ViewHolder(binding.root) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { val inflater = LayoutInflater.from(parent.context) val binding = ScheduleFloorDialogItemBinding.inflate(inflater, parent, false) return MyViewHolder(binding) } @SuppressLint("SetTextI18n") override fun onBindViewHolder(holder: MyViewHolder, position: Int) { val currentFloor = floors[position] Timber.d("Current floor: $currentFloor") holder.binding.floorCheckBox.text = "Floor $currentFloor" } // 改这里:返回列表实际元素数量 override fun getItemCount(): Int { return floors.size } fun getAllFloors(floorsReceived: List<String>) { Timber.d("Floors received : $floorsReceived") this.floors = floorsReceived // 加这句:通知RecyclerView刷新数据 notifyDataSetChanged() } }
2. 修正Dialog Fragment的列表重复问题
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 获取所有楼层列表 createProfileViewModel.totalFloors.observe(viewLifecycleOwner) { Timber.d("List of floors received : $it") val intList = it.map(String::toInt) val maxFloorValue = intList.last() var count = 0 // 每次都新建列表,避免重复添加 val floorList: MutableList<String> = mutableListOf() try { while (count <= maxFloorValue) { floorList.add(count.toString()) count++ } } catch (e: Exception) { Timber.d("Exception: $e") } floorProfileDialogAdapter.getAllFloors(floorList) Timber.d("Floor List : $floorList") } }
额外提个优化点
不要在onCreateView里直接实例化ViewModel,应该用ViewModelProvider来获取,这样ViewModel的生命周期才会和Fragment绑定,不会出现意外销毁的情况:
createProfileViewModel = ViewModelProvider(this)[CreateProfileViewModel::class.java]
内容的提问来源于stack exchange,提问作者oyeraghib
相关产品推荐
相关产品推荐

