RecyclerView中主Toggle与子Toggle联动逻辑完善咨询
看起来你已经搭好了主Toggle和子Toggle联动的基础框架,但目前还有两个关键场景没覆盖到:主Toggle关闭时同步所有子Toggle的状态,以及子Toggle单独切换时自动联动主Toggle的状态。我来帮你把这部分逻辑补全~
一、完善主Toggle的全选/全不选逻辑
你当前的代码只处理了主Toggle开启(isChecked = true)的情况,漏掉了关闭时要同步所有子Toggle为未选中的场景。只需要去掉判断条件,不管主Toggle是开还是关,都调用Adapter的同步方法即可:
switchAll.setOnCheckedChangeListener { _, isChecked -> // 去掉原有的if判断,无论选中状态如何都同步子Toggle binding.recyclervw.post { adapter.onAckAllChecked(isChecked) } }
Adapter里的onAckAllChecked方法可以保留,如果想优化性能,也可以把notifyDataSetChanged()换成notifyItemRangeChanged(0, list.size),减少不必要的视图刷新:
fun onAckAllChecked(isChecked: Boolean) { list.forEach { it.isChecked = isChecked } // 更高效的局部刷新方式 notifyItemRangeChanged(0, list.size) }
二、实现子Toggle联动主Toggle的逻辑
当任意一个子Toggle被关闭时,主Toggle必须自动变为未选中状态;只有当所有子Toggle都被选中时,主Toggle才会变为选中状态。我们可以通过回调接口来实现子Toggle到主Toggle的状态通知:
1. 在Adapter中定义回调接口
首先给Adapter添加一个监听子Toggle状态变化的接口,用于把列表的整体选中状态传递给外部:
class YourAdapter(private var list: MutableList<YourModel>) : RecyclerView.Adapter<YourAdapter.ViewHolder>() { // 定义回调接口 interface OnChildToggleChangeListener { fun onChildToggleStateChanged(isAllChecked: Boolean) } private var toggleChangeListener: OnChildToggleChangeListener? = null // 供外部设置回调的方法 fun setOnChildToggleChangeListener(listener: OnChildToggleChangeListener) { this.toggleChangeListener = listener } // ... 你的ViewHolder和其他原有代码 ... inner class ViewHolder(private val itemBinding: YourItemBinding) : RecyclerView.ViewHolder(itemBinding.root) { fun bindView(position: Int) { val model = list[position] itemBinding.switch.isChecked = model.isChecked // 别忘了初始化子Toggle的状态 itemBinding.switch.setOnCheckedChangeListener { _, isChecked -> model.isChecked = isChecked // 检查当前所有子项是否都被选中 val isAllChecked = list.all { it.isChecked } // 通知外部更新主Toggle的状态 toggleChangeListener?.onChildToggleStateChanged(isAllChecked) } } } }
2. 在Activity/Fragment中实现回调
回到主页面,给Adapter设置回调,根据子Toggle的整体状态更新主Toggle:
// 初始化Adapter后设置回调 adapter.setOnChildToggleChangeListener(object : YourAdapter.OnChildToggleChangeListener { override fun onChildToggleStateChanged(isAllChecked: Boolean) { // 更新主Toggle状态,这里要避免触发主Toggle的监听导致循环刷新 switchAll.isChecked = isAllChecked } })
三、避免循环触发的小优化
当我们通过子Toggle的回调更新主Toggle时,会触发主Toggle的OnCheckedChangeListener,进而又会调用Adapter的onAckAllChecked方法,虽然逻辑上不会出错,但会造成不必要的列表刷新。可以加一个标记变量来避免这种情况:
在Activity/Fragment中定义标记:
private var isUpdatingFromChild = false
修改主Toggle的监听逻辑:
switchAll.setOnCheckedChangeListener { _, isChecked -> // 只有当不是子Toggle触发的主Toggle更新时,才同步子Toggle if (!isUpdatingFromChild) { binding.recyclervw.post { adapter.onAckAllChecked(isChecked) } } }
修改子Toggle的回调逻辑:
override fun onChildToggleStateChanged(isAllChecked: Boolean) { isUpdatingFromChild = true switchAll.isChecked = isAllChecked isUpdatingFromChild = false }
这样就完美解决了循环触发的问题,同时保证了主、子Toggle的状态完全联动~
备注:内容来源于stack exchange,提问作者Nikitha

