调用notifyDataSetChanged时仅RecyclerView最后一项更新的问题
问题与解决方案
问题描述
需要实现一个带选择功能的分段列表,布局包含多个数据动态变化的RecyclerView。每个RecyclerView的列表项带有Checkbox,需求是:当用户选中某一项时,仅该分段的列表项保持启用状态,其他分段的列表项需禁用。但调用notifyDataSetChanged更新RecyclerView时,仅列表的最后一项变为禁用状态,其余项仍为启用状态,调试显示流程正常但界面未匹配预期。
核心问题分析
问题出在Adapter的Binding实例复用上:原Adapter中将LayoutChannelDeviceBinding定义为Adapter的成员变量,所有ViewHolder共享同一个Binding实例。当RecyclerView复用ViewHolder时,前面的项的UI会被最后一次绑定的Binding覆盖,导致只有最后一项能正确更新状态。
解决方案
1. 修正Adapter的ViewHolder绑定逻辑
将LayoutChannelDeviceBinding移至ViewHolder内部,让每个ViewHolder持有独立的Binding实例,避免复用冲突。
2. 优化Checkbox状态更新逻辑
在设置Checkbox状态前先移除监听器,防止状态更新时触发不必要的回调;设置完状态后再重新绑定监听器。
3. 简化MainActivity的数据同步逻辑
直接操作原始的segments列表,无需创建新的alteredList,避免浅拷贝导致的数据同步混乱。
修改后的代码
数据模型(保持不变)
data class SelectionModel( var channelType: String, var channelName: String, var devices: ArrayList<SelectiveDevice>? ) { data class SelectiveDevice( var deviceName: String, var deviceType: String, var selected: Boolean = false ) }
修改后的FiveChannelDeviceAdapter
class FiveChannelDeviceAdapter( private val context: Context, private val callback: FiveChannelDeviceCallback ) : RecyclerView.Adapter<FiveChannelDeviceAdapter.FiveChannelDeviceViewHolder>() { private var mDeviceLists = ArrayList<SelectionModel.SelectiveDevice>() var selectedTypes = "" var selectedGroups = "" var countCheckeds = 0 inner class FiveChannelDeviceViewHolder(private val binding: LayoutChannelDeviceBinding) : RecyclerView.ViewHolder(binding.root) { fun setData(position: Int) { val device = mDeviceLists[position] // 先移除监听器,防止状态更新触发回调 binding.selectDeviceCheck.setOnCheckedChangeListener(null) if (countCheckeds > 0) { if (device.deviceType == selectedTypes) { binding.selectDeviceCheck.isEnabled = true binding.selectDeviceCheck.isClickable = true binding.selectDeviceCheck.isChecked = device.selected binding.tvDeviceName.text = "Enabled" } else { binding.selectDeviceCheck.isEnabled = false binding.selectDeviceCheck.isClickable = false binding.selectDeviceCheck.isChecked = false // 非当前分段项强制取消选中 binding.tvDeviceName.text = "Disabled" } } else { binding.selectDeviceCheck.isEnabled = true binding.selectDeviceCheck.isClickable = true binding.selectDeviceCheck.isChecked = device.selected binding.tvDeviceName.text = "Enabled" } // 重新绑定监听器 binding.selectDeviceCheck.setOnCheckedChangeListener { _, checked -> device.selected = checked if (checked) { countCheckeds += 1 selectedTypes = device.deviceType selectedGroups = selectedTypes callback.setReverseData(selectedTypes, selectedGroups, countCheckeds, mDeviceLists) } else { countCheckeds -= 1 if (countCheckeds == 0) { selectedTypes = "" selectedGroups = "" callback.resetSelection() } else { callback.setReverseData(selectedTypes, selectedGroups, countCheckeds, mDeviceLists) } } } } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FiveChannelDeviceViewHolder { val binding = LayoutChannelDeviceBinding.inflate( LayoutInflater.from(parent.context), parent, false ) return FiveChannelDeviceViewHolder(binding) } override fun onBindViewHolder(holder: FiveChannelDeviceViewHolder, position: Int) { holder.setData(position) } override fun getItemCount(): Int { return mDeviceLists.size } fun setAdapterData(mDeviceList: ArrayList<SelectionModel.SelectiveDevice>, selectedType: String, selectedGroup: String, countChecked: Int) { this.mDeviceLists.clear() this.mDeviceLists.addAll(mDeviceList) this.selectedTypes = selectedType this.selectedGroups = selectedGroup this.countCheckeds = countChecked notifyDataSetChanged() } interface FiveChannelDeviceCallback { fun resetSelection() fun setReverseData(selectedType: String, selectedGroup: String, countChecked: Int, deviceList: ArrayList<SelectionModel.SelectiveDevice>) } }
修改后的MainActivity的setReverseData方法
override fun setReverseData( selectedType: String, selectedGroup: String, countChecked: Int, deviceList: ArrayList<SelectionModel.SelectiveDevice> ) { this.selectedType = selectedType this.selectedGroup = selectedGroup this.countChecked = countChecked // 直接更新原始segments中的对应分组数据 segments.find { it.channelType.equals(selectedType, true) }?.let { it.devices = deviceList } // 更新所有Adapter的数据 fiveChannelDeviceAdapter.setAdapterData(segments[0].devices!!, selectedType, selectedGroup, countChecked) threeChannelDeviceAdapter.setAdapterData(segments[1].devices!!, selectedType, selectedGroup, countChecked) oneChannelDeviceAdapter.setAdapterData(segments[2].devices!!, selectedType, selectedGroup, countChecked) }
内容的提问来源于stack exchange,提问作者Sushant Yadav
相关产品推荐
相关产品推荐

