RadioGroup和RadioButton解析,以及它们的监听器

来源:互联网 发布:淘宝劳保用品 编辑:程序博客网 时间:2024/06/07 02:36

RadioButton继承于CompoundButton,它与CheckBox的区别是不能通过点击自己在选中与未选中状态之间切换,但是CheckBox可以。
RadioGroup是放置RadioButton的容器,同一时刻在同一个RadioGroup中的一组RadioButton中只有一个处于checked状态。


RadioButton的父类CompoundButton定义了一个OnCheckedChangeListener监听器,针对这个监听器有两个成员:mOnCheckedChangeListener和mOnCheckedChangeWidgetListener【供内部使用的】

看下RadioButton的setChecked(boolean checked)方法:

public void setChecked(boolean checked) {        if (mChecked != checked) {            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);            }            mBroadcasting = false;                    }    }

主要的操作是:
1.调用refreshDrawableState()改变RadioButton的背景图片;
2.若mOnCheckedChangeListener!=null 则调用mOnCheckedChangeListener.onCheckedChanged(this, mChecked),同时如果mOnCheckedChangeWidgetListener!=null,调用mOnCheckedChangeWidgetListener.onCheckedChanged(this, mChecked)


RadioGroup内部定义了一个OnCheckedChangeListener监听器,主要是处理RadioButton切换的监听响应。

在RadioGroup的构造方法中调用了init()方法:

private void init() {        mChildOnCheckedChangeListener = new CheckedStateTracker();        mPassThroughListener = new PassThroughHierarchyChangeListener();        super.setOnHierarchyChangeListener(mPassThroughListener);    }

init()内部设置了super.setOnHierarchyChangeListener(mPassThroughListener)

其中mPassThroughListener是一个自定义的继承于ViewGroup.OnHierarchyChangeListener的监听器。

在该监听器的onChildViewAdded(View parent, View child)方法中调用了((RadioButton) child).setOnCheckedChangeWidgetListener( mChildOnCheckedChangeListener)即为当前添加的RadioButton控件设置了mOnCheckedChangeWidgetListener。

mChildOnCheckedChangeListener是CheckedStateTracker类的实例,也是在init()中初始化的。

CheckedStateTracker继承于CompoundButton.OnCheckedChangeListener,该类的onCheckedChanged(CompoundButton buttonView, boolean isChecked)方法内部主要是执行了以下步骤:
1.调用setCheckedStateForView(mCheckedId, false)

    private void setCheckedStateForView(int viewId, boolean checked) {        View checkedView = findViewById(viewId);        if (checkedView != null && checkedView instanceof RadioButton) {            ((RadioButton) checkedView).setChecked(checked);        }    }

将之前选中的radiobutton的id:mCheckedId对应设置为未选中【这里有个地方需要注意的是:在这之前需要将变量mProtectFromCheckedChange设置为了true,因为在((RadioButton) checkedView).setChecked(false);之后必然会调用RadioButton的onCheckedChanged()(在RadioGroup的构造方法中就为添加的RadioButton设置的监听器),引起死循环的递归调用】

2.调用setCheckedId()

    private void setCheckedId(@IdRes int id) {        mCheckedId = id;        if (mOnCheckedChangeListener != null) {            mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId);        }    }

将当前buttonView对应的id通过调用setCheckedId(id)设置为选中状态,此时如果RadioGroup的mOnCheckedChangeListener不为null则调用mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId)执行状态监听器的监听方法。

现在看下RadioGroup的check(int id)方法:

    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);    }

1.如果当前选中的RadioButton的id与mCheckedId相等则直接返回;
2.mCheckedId 不等于-1则说明之前有选中的RadioButton则调用setCheckedStateForView(mCheckedId, false) 将其变为unchecked状态。【因为RadioButton的状态改变了,所以会调用RadioButton的onCheckedChanged(CompoundButton buttonView, boolean isChecked)方法(在RadioGroup的构造方法中我RadioButton设置的监听器),即CheckedStateTracker中的onCheckedChanged(CompoundButton buttonView, boolean isChecked)方法】
3.如果id不等于-1则说明是有效id,则调用setCheckedStateForView(mCheckedId, false) 将其变为checked状态。【因为RadioButton的状态改变了,所以会调用RadioButton的onCheckedChanged(CompoundButton buttonView, boolean isChecked)方法(在RadioGroup的构造方法中我RadioButton设置的监听器),即CheckedStateTracker中的onCheckedChanged(CompoundButton buttonView, boolean isChecked)方法】
4.调用setCheckedId(id) 方法将mCheckedId 赋值为id,同时如果RadioGroup的mOnCheckedChangeListener != null 则调用起mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId) 方法

综上分析,RadioGroup类其实已经为添加在其中的每一个RadioButton的成员变量mOnCheckedChangeWidgetListener赋值了,即为RadioButton设置了内部使用的OnCheckedChangeListener。


通过代码往RadioGroup中添加RadioButton的时候,如果写成:

mRgProductCategories.setOnCheckedChangeListener(this);//默认选中index位置的产品种类          mRgProductCategories.check(mRgProductCategories.getChildAt(index).getId());

会导致RadioGroup的onCheckedChanged()监听方法执行2次【mCheckedId等于-1时是2次,如果之前mCheckedId不等于-1会是3次】,改为下面这样就可以避免上面的情况发生:

mRgProductCategories.setOnCheckedChangeListener(this);            //默认选中index位置的产品种类         ((RadioButton)mRgProductCategories.getChildAt(index)).setChecked(true);

还有一个问题是如果:

mRgProductCategories.setOnCheckedChangeListener(this);mRgProductCategories.clearCheck();

先设置监听器在清楚所有点击态,会导致在setCheckedStateForView(mCheckedId, false); 之后调用一次RadioGroup的onCheckChanged(RadioGroup group, int checkedId)方法。

如果不想调用把上面两个方法交换一下位置即可:

mRgProductCategories.clearCheck();mRgProductCategories.setOnCheckedChangeListener(this);