美文网首页手机移动程序开发Android开发android 开发程序员
四行代码解决RadioGroup.clearCheck()方法返

四行代码解决RadioGroup.clearCheck()方法返

作者: wo叫天然呆 | 来源:发表于2017-10-25 17:05 被阅读358次

    四行代码解决RadioGroup.clearCheck()方法返回两次onCheckedChanged

    本文原创,转载请注明出处。欢迎关注我的 简书

    场景

    当我们使用RadioGroup一般都会设置OnCheckedChangeListener,比如下面这种方式

            mScRadioGroup.setOnCheckedChangeListener(new ScRadioGroup.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(ScRadioGroup group, @IdRes int checkedId) {
                    switch (checkedId) {
                        case R.id.radiobtn_1:
                            break;
                        case R.id.radiobtn_2:
                            break;
                        default:
                            break;
                    }
                }
            });
    

    然而,在调用RadioGroup.clearCheck()方法的时候,你会发现onCheckedChanged回调了两次,一次是之前设置的RadioButton的id,一次是id=-1。
    如果我们把一些逻辑写在onCheckedChanged中,就会比较尴尬,百度搜索了下,通常的做法是直接使用下面这种方式设置按钮是否被点击,废弃掉onCheckedChanged这个回调

    ((RadioButton) ScRadioGroup.findViewById(R.id.radiobtn_1)).setChecked(true);
    

    今天,我要教大家另外一种方法,四行代码搞定这个问题。
    在说方法之前,让我们先来了解下为什么调用clearCheck()方法的时候会触发两次回调?

    源码分析

    1.RadioButton选中状态变更监听器:

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

    查看RadioGroup源码,在init()方法里面我们可以看到这样一行

            mChildOnCheckedChangeListener = new CheckedStateTracker();
    

    查看CheckedStateTracker()就是RadioButton选中状态变更监听器,我们继续往下看

        private class CheckedStateTracker implements
                CompoundButton.OnCheckedChangeListener {
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                // prevents from infinite recursion
                if (mProtectFromCheckedChange) {
                    return;
                }
                mProtectFromCheckedChange = true;
                //mCheckedId 表示之前选中的RadioButton的id
                if (mCheckedId != -1) {
                    //该方法就是取消相对应RadioButton的选中状态
                    setCheckedStateForView(mCheckedId, false);
                }
                mProtectFromCheckedChange = false;
                int id = buttonView.getId();
                //该方法是设置mCheckedId  = id,并且回调onCheckedChanged()
                setCheckedId(id);
            }
        }
    

    从代码上我们可以知道,CheckedStateTracker()就是监听RadioButton选中状态的变更,那么当我们调用clearCheck()的时候,是不是会触发它呢?打印下log你就知道了!这里我就不贴log了。

    2.clearCheck()方法具体都做了什么:

        public void clearCheck() {
            check(-1);
        }
    

    真简单,只做了一件事,就是穿一个id=-1到check()方法里面去,我们接着往下看

        public void check(int id) {
            // don't even bother
            if (id != -1 && (id == mCheckedId)) {
                return;
            }
            //这里会先把所有按钮设置成false
            if (mCheckedId != -1) {
                setCheckedStateForView(mCheckedId, false);
            }
            //然后设置指定id的按钮变成true
            if (id != -1) {
                setCheckedStateForView(id, true);
            }
            //最后通知监听器
            setCheckedId(id);
        }
    

    大家看到了把,这里先是把之前设置的RadioButton变更成未选中状态,然后再将这次设置的RadioButton变更成选中状态,最后通知监听器并设置mCheckedId。
    顺便贴下setCheckedId()方法给大家看吧

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

    看到了没,就这么简单。

    3.分析问题:

    好了,源码分析完毕,现在我们来分析下为什么会产生2次回调。
    通过之前的源码,我们可以看到,当调用setCheckedStateForView()方法的时候,会触发CheckedStateTracker()。不信?你打印log看看。
    当我们调用clearCheck()方法清空选中项的时候,他先会触发一次CheckedStateTracker(),在该方法里面会调用一次setCheckedId(),然后再check()方法最后又会调用一次setCheckedId(),总共两次。问题找到了!!鲜花刷起来,掌声在哪里?666刷起来!!

    4.解决问题:

    既然已经找到问题所在了,那么现在我来教大家四行代码解决这个问题,不啰嗦了,直接上代码直观点

        private boolean mClearClick = false;//判断是否是来自ClearClick()方法
    
        private void setCheckedId(int id) {
            mCheckedId = id;
            if (mOnCheckedChangeListener != null) {
                mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId);
            }
            mClearClick = false;
        }
    
        public void clearCheck() {
            /**
             * 解决调用RadioGroup的clearCheck()方法,onCheckedChanged方法仍被执行
             * 在clearCheck开启mClearClick
             * 在CheckedStateTracker中判断mClearClick是否为true,是的话不去调用setCheckedId方法
             * 在setCheckedId方法里面关闭mClearClick
             */
            mClearClick = true;
            check(-1);
        }
    
    
        private class CheckedStateTracker implements
                CompoundButton.OnCheckedChangeListener {
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                // prevents from infinite recursion
                if (mProtectFromCheckedChange) {
                    return;
                }
                mProtectFromCheckedChange = true;
                if (mCheckedId != -1) {
                    setCheckedStateForView(mCheckedId, false);
                }
                mProtectFromCheckedChange = false;
                int id = buttonView.getId();
                if (!mClearClick) {
                    setCheckedId(id);
                }
            }
        }
    

    解决了,查看下,当调用clearCheck()方法的时候,只会返回一次回调,是id=-1的回调。O啦!

    欢迎大家留言指出我的不足。

    相关文章

      网友评论

        本文标题:四行代码解决RadioGroup.clearCheck()方法返

        本文链接:https://www.haomeiwen.com/subject/sroqpxtx.html