Change Checkbox value without triggering onCheckChanged

前端 未结 19 980
广开言路
广开言路 2020-11-30 00:18

I have setOnCheckedChangeListener implemented for my checkbox

Is there a way I can call

checkbox.setChecked(false);
         


        
相关标签:
19条回答
  • 2020-11-30 00:32

    I guess using reflection is the only way. Something like this:

    CheckBox cb = (CheckBox) findViewById(R.id.checkBox1);
    try {
        Field field = CompoundButton.class.getDeclaredField("mChecked");
        field.setAccessible(true);
        field.set(cb, cb.isChecked());
        cb.refreshDrawableState();
        cb.invalidate();
    } catch (Exception e) {
        e.printStackTrace();
    }
    
    0 讨论(0)
  • 2020-11-30 00:33

    You could use this SafeCheckBox class as your checkbox :

    public class SafeCheckBox extends AppCompatCheckBox implements CompoundButton.OnCheckedChangeListener {
    
        private OnSafeCheckedListener onSafeCheckedListener;
    
        private int mIgnoreListener = CALL_LISTENER;
    
        public static final int IGNORE = 0;
        public static final int CALL_LISTENER = 1;
    
        @Retention(RetentionPolicy.SOURCE)
        @IntDef({IGNORE, CALL_LISTENER})
        public @interface ListenerMode {
        }
    
        public SafeCheckBox(Context context) {
            super(context);
            init(context);
        }
    
        public SafeCheckBox(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context);
        }
    
        public SafeCheckBox(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init(context);
        }
    
        /**
         * @param checkState     change state of the checkbox to 
         * @param mIgnoreListener true to ignore the listener else listener will be  notified
         */
        public void setSafeCheck(boolean checkState, @ListenerMode int mIgnoreListener) {
            if (isChecked() == checkState) return; //already in the same state no need to fire listener. 
    
            if (onSafeCheckedListener != null) { // this to avoid a bug if the user listens for the event after using this method and in that case he will miss first check
                this.mIgnoreListener = mIgnoreListener;
            } else {
                this.mIgnoreListener = CALL_LISTENER;
            }
            setChecked(checkState);
        }
    
        private void init(Context context) {
            setOnCheckedChangeListener(this);
        }
    
    
        public OnSafeCheckedListener getOnSafeCheckedListener() {
            return onSafeCheckedListener;
        }
    
        public void setOnSafeCheckedListener(OnSafeCheckedListener onSafeCheckedListener) {
            this.onSafeCheckedListener = onSafeCheckedListener;
        }
    
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    
            if (onSafeCheckedListener != null)
                onSafeCheckedListener.onAlwaysCalledListener(buttonView, isChecked);// this has to be called before onCheckedChange
            if (onSafeCheckedListener != null && (mIgnoreListener == CALL_LISTENER)) {
                onSafeCheckedListener.onCheckedChanged(buttonView, isChecked);
            }
            mIgnoreListener = CALL_LISTENER;
        }
    
        /**
         * Listener that will be called when you want it to be called.
         * On checked change listeners are called even when the setElementChecked is called from code. :(
         */
        public interface OnSafeCheckedListener extends OnCheckedChangeListener {
            void onAlwaysCalledListener(CompoundButton buttonView, boolean isChecked);
        }
    }
    
    • Then you could call :-

      setSafeCheck(true,ListenerMode.IGNORE);// OnCheckedChange listener will not be notified

    0 讨论(0)
  • 2020-11-30 00:34

    No, you can't do it. The onCheckedChanged method is called directly from setChecked. What you can do is the following:

    mCheck.setOnCheckedChangeListener (null);
    mCheck.setChecked (false);
    mCheck.setOnCheckedChangeListener (mListener);
    

    See the source of CheckBox, and the implementation of setChecked:

    public void  setChecked(boolean checked) {
        if (mChecked != checked) {
            mChecked = checked;
            refreshDrawableState();
    
            // 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;            
        }
    }
    
    0 讨论(0)
  • 2020-11-30 00:34

    Another possible way to achieve this is by using a custom CheckBox , which will let you choose if you want the listener to be called or not :

    public class CheckBox extends AppCompatCheckBox {
        private OnCheckedChangeListener mListener;
    
        public CheckBox(final Context context) {
            super(context);
        }
    
        public CheckBox(final Context context, final AttributeSet attrs) {
            super(context, attrs);
        }
    
        public CheckBox(final Context context, final AttributeSet attrs, final int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @Override
        public void setOnCheckedChangeListener(final OnCheckedChangeListener listener) {
            mListener = listener;
            super.setOnCheckedChangeListener(listener);
        }
    
        public void setChecked(final boolean checked, final boolean alsoNotify) {
            if (!alsoNotify) {
                super.setOnCheckedChangeListener(null);
                super.setChecked(checked);
                super.setOnCheckedChangeListener(mListener);
                return;
            }
            super.setChecked(checked);
        }
    
        public void toggle(boolean alsoNotify) {
            if (!alsoNotify) {
                super.setOnCheckedChangeListener(null);
                super.toggle();
                super.setOnCheckedChangeListener(mListener);
            }
            super.toggle();
        }
    }
    

    Kotlin version, if you prefer:

    class CheckBox @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : AppCompatCheckBox(context, attrs, defStyleAttr) {
        private var listener: CompoundButton.OnCheckedChangeListener? = null
    
        override fun setOnCheckedChangeListener(listener: CompoundButton.OnCheckedChangeListener?) {
            this.listener = listener
            super.setOnCheckedChangeListener(listener)
        }
    
        fun setChecked(checked: Boolean, alsoNotify: Boolean) {
            if (!alsoNotify) {
                super.setOnCheckedChangeListener(null)
                super.setChecked(checked)
                super.setOnCheckedChangeListener(listener)
                return
            }
            super.setChecked(checked)
        }
    
        fun toggle(alsoNotify: Boolean) {
            if (!alsoNotify) {
                super.setOnCheckedChangeListener(null)
                super.toggle()
                super.setOnCheckedChangeListener(listener)
            }
            super.toggle()
        }
    }
    

    sample usage:

    checkBox.setChecked(true,false);
    
    0 讨论(0)
  • 2020-11-30 00:36

    How about this. Try to use Tag in View

    mCheck.setTag("ignore");
    mCheck.setChecked(true);
    mCheck.setTag(null);
    

    and

    switch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean selected) {
    
                //If switch has a tag, ignore below
                if(compoundButton.getTag() != null)
                    return; 
    
                if (selected) {
                    // do something
                } else {
                    // do something else
                }
    
            }
        });
    
    0 讨论(0)
  • 2020-11-30 00:36

    My solution written in java based on @Chris answer:

    chkParent.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    if(buttonView.getTag() != null){
                        buttonView.setTag(null);
                        return;
                    }
                    if(isChecked){
                        chkChild.setTag(true);
                        chkChild.setChecked(false);
                    }
                    else{
                        chkParent.setChecked(true);
                    }
                }
    });
    
    chkChild.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    if(buttonView.getTag() != null){
                        buttonView.setTag(null);
                        return;
                    }
                    if(isChecked){
                        chkParent.setTag(true);
                        chkParent.setChecked(false);
                    }
                    else{
                        chkChild.setChecked(true);
                    }
                }
    });
    

    2 checkboxes and always one will be checked (one be must checked initially though). Setting tag to true blocks onCheckedChanged listener.

    0 讨论(0)
提交回复
热议问题