在Kotlin中按两个条件对RecyclerView排序的问题
RecyclerView按checked与order规则排序的实现方案
问题需求
需要实现RecyclerView元素排序逻辑:
- 未勾选(
checked=false)的元素排在上方,已勾选(checked=true)的排在下方 - 同状态下的元素,按照
order字段的整数升序排列 - 勾选/取消勾选时,元素自动移动到对应排序位置,比如勾选order=2的元素时,要插入到已勾选区域order=1和3之间;取消勾选order=5的元素时,要回到未勾选区域的对应order位置
现有代码的问题
当前的unchecked()和checked()方法手动对集合进行增删移动元素,再调用sort()会导致集合操作冲突:
- 手动增删元素后再排序,容易出现元素重复、位置错乱
notifyItemMoved和notifyDataSetChanged混用,刷新逻辑冲突,导致UI异常
解决步骤
- 移除手动移动元素的逻辑:先修改目标元素的
checked状态,再统一排序 - 使用DiffUtil实现局部刷新:替代全量的
notifyDataSetChanged,既保证UI正确性,又提升性能 - 统一排序规则:用
compareBy({ it.checked }, { it.order })作为排序逻辑,注意checked是布尔值,false会排在true前面(符合未勾选在上的需求)
完整代码示例
1. 数据类Model
data class Model( val id: Int, // 必须要有唯一标识,用于DiffUtil判断元素唯一性 var checked: Boolean, val order: Int )
2. Adapter修改后的代码
class MyAdapter : RecyclerView.Adapter<MyAdapter.ViewHolder>() { private var list = mutableListOf<Model>() // 实现DiffUtil的ItemCallback,用于精准计算列表差异 private val diffCallback = object : DiffUtil.ItemCallback<Model>() { override fun areItemsTheSame(oldItem: Model, newItem: Model): Boolean { return oldItem.id == newItem.id // 用唯一id判断是否为同一元素 } override fun areContentsTheSame(oldItem: Model, newItem: Model): Boolean { return oldItem == newItem // 数据类直接用==判断内容是否一致 } } // 刷新列表的核心方法:用DiffUtil处理局部刷新 private fun updateList(newList: List<Model>) { val diffResult = DiffUtil.calculateDiff(object : DiffUtil.Callback() { override fun getOldListSize() = list.size override fun getNewListSize() = newList.size override fun areItemsTheSame(oldPos: Int, newPos: Int): Boolean { return list[oldPos].id == newList[newPos].id } override fun areContentsTheSame(oldPos: Int, newPos: Int): Boolean { return list[oldPos] == newList[newPos] } }) list.clear() list.addAll(newList) diffResult.dispatchUpdatesTo(this) } // 处理勾选状态切换的方法 fun toggleChecked(position: Int) { // 1. 修改当前元素的checked状态 val item = list[position] val updatedItem = item.copy(checked = !item.checked) list[position] = updatedItem // 2. 按照规则对列表重新排序 val sortedList = list.sortedWith( compareBy<Model> { it.checked }.thenBy { it.order } ).toMutableList() // 3. 用DiffUtil更新列表 updateList(sortedList) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(parent.context) .inflate(R.layout.item_layout, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val item = list[position] holder.checkBox.isChecked = item.checked // 避免刷新视图时重复触发状态切换 holder.checkBox.setOnCheckedChangeListener { _, isChecked -> if (isChecked != item.checked) { toggleChecked(position) } } // 绑定其他视图内容... } override fun getItemCount() = list.size inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val checkBox: CheckBox = itemView.findViewById(R.id.checkbox) // 其他视图组件... } }
关键说明
- DiffUtil的作用:通过对比新旧列表的差异,只刷新状态或位置变化的元素,避免全量刷新导致的UI闪烁和位置异常
- 排序逻辑:
compareBy({ it.checked }, { it.order })中,布尔值false(未勾选)排序优先级高于true(已勾选),所以未勾选元素自动排在上方;同状态下按order升序排列,完全匹配需求 - 避免重复触发:在
setOnCheckedChangeListener中增加状态判断,防止视图刷新重新绑定时重复调用toggleChecked
内容的提问来源于stack exchange,提问作者DJTalls
相关产品推荐
相关产品推荐

