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

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);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值