RecyclerView 为了提高效率,其内部使用了Recycler回收机制,原理是将上方的 itemView 衔接下次使用,仅加载数据,从而达到View的复用。
问题描述
项目中涉及 RecyclerView 中包含 CheckBox 时。
- 首先遇到是焦点问题,主要是CheckBox抢占了焦点,造成点击事件无法响应的问题。
- 其次遇到CheckBox多选时,滑动状态错乱问题
焦点解决方案
为父级元素设置 android:descendantFocusability
属性值 | 作用 |
---|---|
beforeDescendants | viewgroup会优先其子类控件而获取到焦点 |
afterDescendants | viewgroup只有当其子类控件不需要获取焦点时才获取焦点 |
blocksDescendants | viewgroup会覆盖子类控件而直接获得焦点 |
状态错乱解决方案
方案一
原理:使用SparseBooleanArray(少量数据比HashMap效率更高,原因是其key的类型为 int 型,相比HashMap少了封箱过程)存储选中的状态值,根据position获取。
//存储选中状态
private SparseBooleanArray mCheckStates = new SparseBooleanArray();
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
holder.checkBox.setTag(position);
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> {
int poi = (int) v.getTag();
if (isChecked) {
mCheckStates.put(poi, true);
} else {
mCheckStates.delete(poi);
}
});
holder.checkBox.setChecked(mCheckStates.get(position));
}
/** 设置全部选中否 */
public void setSelectAll(boolean selectAll) {
if (list != null) {
for (int i = 0; i < list.size(); i++) {
mCheckStates.put(i, selectAll);
}
notifyDataSetChanged();
}
}
方案二
原理:将CheckBox 的OnCheckedChangeListener 置空(因为当view滑出致checkbox 的可视范围时,OnCheckedChangeListener事件会被触发),重新设置OnCheckedChangeListener事件。
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
holder.checkBox.setOnCheckedChangeListener(null);
holder.checkBox.setChecked(data.isSelect());
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> data.setSelect(isChecked));
holder.item.setOnClickListener(v -> {
data.setSelect(!holder.checkBox.isChecked());
holder.checkBox.setChecked(data.isSelect());
});
}
/** 设置全部选中否 */
public void setSelectAll(boolean selectAll) {
if (list != null) {
for (int i = 0; i < list.size(); i++) {
list.get(i).setSelect(selectAll);
}
notifyDataSetChanged();
}
}
注:以上便是RecyclerView和CheckBox的坑,具体使用哪个方案可根据实际问题选择。