四行代碼解決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啦!
歡迎大家留言指出我的不足。