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