記錄RadioGroup和RadioButton的單選實現原理
很多時候,需要在開發中需要實現類似RadioGroup和RadioButton結合使用實現的單選效果,但是RadioGroup和RadioButton結合的實現效果不能完全定製化,不能適應所有的APP開發需求,這裡簡單的剖析下RadioGroup的實現原理,來完成自定義RadioGroup顯示單選。
在之前寫這種實現的時候,因為沒有閱讀過原始碼,實現的比較挫,簡單的用一個List存放所有的單元Item,然後在點選事件中,遍歷所有的View,挨個設定點選事件,雖然也能實現點選效果但是效率低的不行,而且感覺寫起來就是一種垃圾程式碼,寫過幾次之後,決定研究下原生的程式碼寫法,脫離低階,向優秀進發。
下面看一下RadioGroup的原始碼吧:
首先RadioGroup是繼承的LinearLayout,這就很多時候限制了我們一些開發的實現
public class RadioGroup extends LinearLayout
如果自定義的話,這東西就靈活多了
實現單選的關鍵部分是
private int mCheckedId = -1;
這個是用來儲存單選選中的view的id值的,
RadioGroup重寫了addView方法
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
if (child instanceof RadioButton) {
final RadioButton button = (RadioButton) child;
if (button.isChecked()) {
mProtectFromCheckedChange = true;
if (mCheckedId != -1) {
setCheckedStateForView(mCheckedId, false);
}
mProtectFromCheckedChange = false ;
setCheckedId(button.getId());
}
}
super.addView(child, index, params);
}
可以看到首先RadioGroup會先判斷子view是否是RadioButton,如果是,判斷是否預設已經被選擇
private void setCheckedStateForView(int viewId, boolean checked) {
View checkedView = findViewById(viewId);
if (checkedView != null && checkedView instanceof RadioButton) {
((RadioButton) checkedView).setChecked(checked);
}
}
setCheckedStateForView(int viewId, boolean checked)方法是用來實現點選效果的,這個地方很好理解。
那麼addView剩下的只剩一個setCheckedId(id)這個方法了
private void setCheckedId(@IdRes int id) {
mCheckedId = id;
if (mOnCheckedChangeListener != null) {
mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId);
}
}
這裡就是實現之前說的,將單選的view的id賦值給mCheckedId ,並且,額外的,綁定了監聽器
在addView之後,RadioGroup重新了這個方法
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// checks the appropriate radio button as requested in the XML file
if (mCheckedId != -1) {
mProtectFromCheckedChange = true;
setCheckedStateForView(mCheckedId, true);
mProtectFromCheckedChange = false;
setCheckedId(mCheckedId);
}
}
這個方法是確保在整個addView結束後,最終確定顯示被點選的Item;
單選電機的實現原理主要是以下這個方法:
public void check(@IdRes int id) {
// don't even bother
if (id != -1 && (id == mCheckedId)) {
return;
}
if (mCheckedId != -1) {
setCheckedStateForView(mCheckedId, false);
}
if (id != -1) {
setCheckedStateForView(id, true);
}
setCheckedId(id);
}
這裡的意思就是,首先確定點選的是原來被選中的項,不作處理,如果是新的項,那麼先把原來的項的點選顯示變成false,之後,把當前電機的項的顯示變為顯示狀態,之後,將當前點選的id賦值給mCheckedId;
那麼最後總結下邏輯:
首先,初始化mCheckedId=-1,然後addView中處理預設的點選項,點選項的id傳給mCheckedId,當每次點選新的項時,根據mCheckedId,將原來的點選項置為未點選,然後根據新的項的id,把新的項置為點選,最後,新的項的id賦值給mCheckedId,來維護單選。