四行代码解决RadioGroup.clearCheck()方法返回两次onCheckedChanged
场景
当我们使用RadioGroup一般都会设置OnCheckedChangeListener,比如下面这种方式
mScRadioGroup.setOnCheckedChangeListener(new ScRadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(ScRadioGroup group, @IdRes int checkedId) {
switch (checkedId) {
case R.id.radiobtn_1:
break;
case R.id.radiobtn_2:
break;
default:
break;
}
}
});
然而,在调用RadioGroup.clearCheck()方法的时候,你会发现onCheckedChanged回调了两次,一次是之前设置的RadioButton的id,一次是id=-1。
如果我们把一些逻辑写在onCheckedChanged中,就会比较尴尬,百度搜索了下,通常的做法是直接使用下面这种方式设置按钮是否被点击,废弃掉onCheckedChanged这个回调
((RadioButton) ScRadioGroup.findViewById(R.id.radiobtn_1)).setChecked(true);
今天,我要教大家另外一种方法,四行代码搞定这个问题。
在说方法之前,让我们先来了解下为什么调用clearCheck()方法的时候会触发两次回调?
源码分析
1.RadioButton选中状态变更监听器:
private void init() {
mChildOnCheckedChangeListener = new CheckedStateTracker();
mPassThroughListener = new PassThroughHierarchyChangeListener();
super.setOnHierarchyChangeListener(mPassThroughListener);
}
查看RadioGroup源码,在init()方法里面我们可以看到这样一行
mChildOnCheckedChangeListener = new CheckedStateTracker();
查看CheckedStateTracker()
就是RadioButton选中状态变更监听器,我们继续往下看
private class CheckedStateTracker implements
CompoundButton.OnCheckedChangeListener {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// prevents from infinite recursion
if (mProtectFromCheckedChange) {
return;
}
mProtectFromCheckedChange = true;
//mCheckedId 表示之前选中的RadioButton的id
if (mCheckedId != -1) {
//该方法就是取消相对应RadioButton的选中状态
setCheckedStateForView(mCheckedId, false);
}
mProtectFromCheckedChange = false;
int id = buttonView.getId();
//该方法是设置mCheckedId = id,并且回调onCheckedChanged()
setCheckedId(id);
}
}
从代码上我们可以知道,CheckedStateTracker()
就是监听RadioButton选中状态的变更,那么当我们调用clearCheck()
的时候,是不是会触发它呢?打印下log你就知道了!这里我就不贴log了。
2.clearCheck()方法具体都做了什么:
public void clearCheck() {
check(-1);
}
真简单,只做了一件事,就是穿一个id=-1到check()
方法里面去,我们接着往下看
public void check(int id) {
// don't even bother
if (id != -1 && (id == mCheckedId)) {
return;
}
//这里会先把所有按钮设置成false
if (mCheckedId != -1) {
setCheckedStateForView(mCheckedId, false);
}
//然后设置指定id的按钮变成true
if (id != -1) {
setCheckedStateForView(id, true);
}
//最后通知监听器
setCheckedId(id);
}
大家看到了把,这里先是把之前设置的RadioButton变更成未选中状态,然后再将这次设置的RadioButton变更成选中状态,最后通知监听器并设置mCheckedId。
顺便贴下setCheckedId()
方法给大家看吧
private void setCheckedId(int id) {
mCheckedId = id;
if (mOnCheckedChangeListener != null) {
mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId);
}
}
看到了没,就这么简单。
3.分析问题:
好了,源码分析完毕,现在我们来分析下为什么会产生2次回调。
通过之前的源码,我们可以看到,当调用setCheckedStateForView()
方法的时候,会触发CheckedStateTracker()
。不信?你打印log看看。
当我们调用clearCheck()
方法清空选中项的时候,他先会触发一次CheckedStateTracker()
,在该方法里面会调用一次setCheckedId()
,然后再check()
方法最后又会调用一次setCheckedId()
,总共两次。问题找到了!!鲜花刷起来,掌声在哪里?666刷起来!!
4.解决问题:
既然已经找到问题所在了,那么现在我来教大家四行代码解决这个问题,不啰嗦了,直接上代码直观点
private boolean mClearClick = false;//判断是否是来自ClearClick()方法
private void setCheckedId(int id) {
mCheckedId = id;
if (mOnCheckedChangeListener != null) {
mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId);
}
mClearClick = false;
}
public void clearCheck() {
/**
* 解决调用RadioGroup的clearCheck()方法,onCheckedChanged方法仍被执行
* 在clearCheck开启mClearClick
* 在CheckedStateTracker中判断mClearClick是否为true,是的话不去调用setCheckedId方法
* 在setCheckedId方法里面关闭mClearClick
*/
mClearClick = true;
check(-1);
}
private class CheckedStateTracker implements
CompoundButton.OnCheckedChangeListener {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// prevents from infinite recursion
if (mProtectFromCheckedChange) {
return;
}
mProtectFromCheckedChange = true;
if (mCheckedId != -1) {
setCheckedStateForView(mCheckedId, false);
}
mProtectFromCheckedChange = false;
int id = buttonView.getId();
if (!mClearClick) {
setCheckedId(id);
}
}
}
解决了,查看下,当调用clearCheck()
方法的时候,只会返回一次回调,是id=-1的回调。O啦!
欢迎大家留言指出我的不足。