使用多个CheckBox进行联动时,应该使用setOnClickListener替代OnCheckedChangeListener
以下是错误代码(正确代码在文末):
private void initCheckBox(final List<CheckBox> checkBoxList, final CheckBox checkBoxAll) {
// 初始化 各个 子的 ChechBox
for (CheckBox checkBox : checkBoxList) {
checkBox.setOnCheckedChangeListener(newCompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
boolean isAllSelect = true; // 假设全部都已经被选中了
for (CheckBox checkBox : checkBoxList) { // checkBoxList_First
if (!checkBox.isChecked()) { // 只要有一个没被选中 ,就说明不是全部被选中,如果遍历完之后还没有找到,那么就设置为true
isAllSelect = false;
break;
}
}
checkBoxAll.setChecked(isAllSelect);
}
});
}
checkBoxAll.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
for (CheckBox checkBox : checkBoxList) {
checkBox.setChecked(true);
}
} else {
for (CheckBox checkBox : checkBoxList) {
checkBox.setChecked(false);
}
}
}
});
}
这样的代码,乍一看,感觉没什么毛病,不过就是点击全选的时候,则所有的子CheckBox都选中;当所有的子CheckBox都选中时,checkBoxAll选中状态为true,否则,checkBoxAll选中状态为false。但是运行在项目的时候,会发现,当所有的子CheckBox都选中了,虽然此时,checkBoxAll也显示为选中状态,但是当对其中一个子CheckBox取消选中时,所有的子CheckBox都被取消选中了。
经过查看CheckBox的源码,发现,当CheckBox执行setChecked时,会触发执行OnCheckedChangeListener,所以,当所有的子CheckBox都选中后取消其中一个,会执行checkBoxAll.setChecked(false),而此时,会执行checkBoxAll的OnCheckedChangeListener,执行后,则将所有的子CheckBox都变成了取消状态。
public void setChecked(boolean checked) {
if (mChecked != checked) {
mCheckedFromResource = false;
mChecked = checked;
refreshDrawableState();
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
// Avoid infinite recursions if setChecked() is called from a listener
if (mBroadcasting) {
return;
}
mBroadcasting = true;
if (mOnCheckedChangeListener != null) {
mOnCheckedChangeListener.onCheckedChanged(this, mChecked);
}
if (mOnCheckedChangeWidgetListener != null) {
mOnCheckedChangeWidgetListener.onCheckedChanged(this, mChecked);
}
final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
if (afm != null) {
afm.notifyValueChanged(this);
}
mBroadcasting = false;
}
}
这里,考虑使用setOnClickListener来替代setOnCheckedChangeListener,因为setCheck虽然会触发OnCheckedChangeListener,但是不会触发OnClickListener。
所以将checkBoxAll的初始化代码改为以下代码:
checkBoxAll.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!checkBoxAll.isChecked()) { // 未选中时,则选中
checkBoxAll.setChecked(true);
for (CheckBox checkBox : checkBoxList) {
checkBox.setChecked(true);
}
} else { // 选中时,则设置为未选中
checkBoxAll.setChecked(false);
for (CheckBox checkBox : checkBoxList) {
checkBox.setChecked(false);
}
}
}
});
这样,看起来也一样没毛病,完全符合逻辑。但是,当点击checkBoxAll时,会出现checkBoxAll的选中状态一闪而过。
但是,经验证,当CheckBox被点击的时候,setOnCheckedChangeListener会比setOnClickListener先执行,即当手指点击的时候,此时,若原本checkBoxAll是true状态,先执行了setOnCheckedChangeListener,则变成false,而setOnClickListener中的代码后执行,又被设置为true,所以就出现了一闪而过的现象。
好了,说了这么多,最终代码应该为(子CheckBox的OnCheckedChangeListener也可以使用setOnClickListener去处理):
private void initCheckBox(final List<CheckBox> checkBoxList, final CheckBox checkBoxAll) {
// 初始化 各个 子的 ChechBox
for (CheckBox checkBox : checkBoxList) {
checkBox.setOnCheckedChangeListener(newCompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
boolean isAllSelect = true; // 假设全部都已经被选中了
for (CheckBox checkBox : checkBoxList) {
if (!checkBox.isChecked()) { // 只要有一个没被选中 ,就说明 不是全部被选中,如果遍历完之后还没有找到,那么就设置为true
isAllSelect = false;
break;
}
}
checkBoxAll.setChecked(isAllSelect);
}
});
}
checkBoxAll.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (checkBoxAll.isChecked()) {
checkBoxAll.setChecked(true);
for (CheckBox checkBox : checkBoxList) {
checkBox.setChecked(true);
}
} else {
checkBoxAll.setChecked(false);
for (CheckBox checkBox : checkBoxList) {
checkBox.setChecked(false);
}
}
}
});
}
以上是我的个人理解,如果有错误的地方,请帮忙指正,谢谢!