1. 程式人生 > >記錄RadioGroup和RadioButton的單選實現原理

記錄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,來維護單選。