188

I have setOnCheckedChangeListener implemented for my checkbox

Is there a way I can call

checkbox.setChecked(false);

without triggering the onCheckedChanged

Archie.bpgc
  • 23,812
  • 38
  • 150
  • 226
  • Why not just use a simple true/false flag? It's the simplest way to go about this problem and it only takes like three lines of extra code. See my answer below. – Ruchir Baronia Aug 15 '18 at 01:34
  • I think the best solution is given here https://stackoverflow.com/questions/9129858/how-can-i-distinguish-whether-switch-checkbox-value-is-changed-by-user-or-progra/14307643#14307643 – Salman khan Jul 06 '21 at 11:12

21 Answers21

317

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;            
    }
}
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Shade
  • 9,936
  • 5
  • 60
  • 85
  • 7
    How do you propose to get `mListener`? `Checkbox` doesn't have a getter for its `OnCheckChangeListener` – tir38 Jul 08 '14 at 19:56
  • 28
    Well no need to downvote simply because you don't understand the solution. `mListener` is an implementation of the `OnCheckChangedListener` interface, which was created by the programmer. My answer implies that the programmer maintained a reference to **their own implementation** - `mListener`. – Shade Jul 09 '14 at 08:20
  • Would it be inefficient to change the listener if you want to use the setChecked() method repetitively? – Ren Apr 22 '16 at 22:01
  • 4
    @Ren, changing the listener involves only the setting of a property in the `CheckBox` object. I wouldn't say that is inefficient. – Shade Apr 26 '16 at 11:00
  • The doc reads "Called when the checked radio button has changed. When the selection is cleared, checkedId is -1". This is really misleading, it should either have the isChecked passed through as well. – AA_PV Jan 03 '17 at 22:10
  • This is problematic when using DI tools such as ButterKnife. – TheRealChx101 Aug 31 '19 at 21:32
  • @TheRealChx101 the answer is 6+ years old, so it may be a bit outdated, but could you provide an example of what you mean? – Shade Sep 02 '19 at 12:48
  • @Shade ButterKnife injects its own handler so the best way to use your code would be through extending the CheckBox and when the handler is set, you cache it. Then you can add a method that will update the checked state by setting the handler to null (`super.setOnCheckedChangeListener(null)`), calling `super.setChecked(...)`, then re-installing the cached handler again `super.setOnCheckedChangeListener(cachedListener)`. – TheRealChx101 Sep 02 '19 at 15:53
  • @TheRealChx101 Hmm, "injects its own handler" and "the best way is to subclass the system component" don't sound like DI to me :) Anyway, the answer is valid for vanilla Android and I'm happy that you found a way to make it work for you. – Shade Sep 05 '19 at 08:06
  • @Shade What a madlad – DeaMon1 Oct 24 '22 at 20:56
120

Add this code inside OnCheckedChangeListener:

if(!compoundButton.isPressed()) {
            return;
}

This will help us to figure out weather checkBox state was changed programmatically or by user action.

Basha K
  • 1,509
  • 1
  • 11
  • 16
krisDrOid
  • 3,252
  • 3
  • 25
  • 36
  • 17
    be aware! This breaks accessibility mode! isPressed() is not true when it is triggered by a double tap from voice assistant mode. – A1m May 29 '19 at 01:41
  • The only solution that solves a DataBinding problem (see also https://stackoverflow.com/questions/42666559/android-databinding-how-to-avoid-oncheckedchanged-triggered-by-programmatically). – CoolMind Jun 17 '21 at 11:13
31

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);
            return;
        }
        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)
            return
        }
        super.toggle()
    }
}

sample usage:

checkBox.setChecked(true,false);

Now also available on my repository:

https://github.com/AndroidDeveloperLB/CommonUtils

android developer
  • 114,585
  • 152
  • 739
  • 1,270
14

For anyone that stumbles across this, one simpler way to do this is to just use a tag on the checkbox and then check that tag on its listener (code is in Kotlin):

checkBox.tag = false
checkBox.setOnCheckedChangeListener{ buttonView, isChecked -> 
    if(checkBox.tag != true) {
        // Do some stuff
    } else {
        checkBox.tag = false
    }

Then when accessing just set the tag to true before you set the isChecked to true when you want to ignore the value change:

checkBox.tag = true
checkBox.isChecked = true

You could also map the tag to a key by using the alternative setTag method that requires a key if you were worried about understandability. But if its all contained to a single class a few comment strings will be more than enough to explain whats happening.

Boken
  • 4,825
  • 10
  • 32
  • 42
Chris
  • 4,244
  • 1
  • 19
  • 12
11

you use simply setonclickListener , it will works fine and this is very simple method, thanks :)

Rohit
  • 3,401
  • 4
  • 33
  • 60
10

Is very simple, you just check isPressed inside setOnCheckedChangeListener

Kotlin

switch.setOnCheckedChangeListener { buttonView, isChecked ->
    when {        
        buttonView.isPressed -> {
            foo(isChecked)
        }
    }
Joris
  • 486
  • 7
  • 10
  • @NicolasDuponchel, in above answer krisDrOid proposed this solution, and it has a downside. But for DataBinding it is a good variant. – CoolMind Jun 17 '21 at 11:12
8

Using Kotlin's extensions with @Shade answer :

fun CompoundButton.setCustomChecked(value: Boolean,listener: CompoundButton.OnCheckedChangeListener) {
     setOnCheckedChangeListener(null)
     isChecked = value
     setOnCheckedChangeListener(listener)
}
Mickey Mouse
  • 1,731
  • 3
  • 24
  • 40
4

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

Tim Malseed
  • 6,003
  • 6
  • 48
  • 66
krishan
  • 1,141
  • 1
  • 12
  • 24
3

Set null to changeListener before check radio button. You can set listener again after check radio button.

radioGroup.setOnCheckedChangeListener(null);
radioGroup.check(R.id.radioButton);
radioGroup.setOnCheckedChangeListener(new 

RadioGroup.OnCheckedChangeListener() {
   @Override
   public void onCheckedChanged(RadioGroup radioGroup, @IdRes int i) {

   }
});
gencaysahinn
  • 193
  • 2
  • 9
3

My interpretation which i think is the easiest
May be helpful)

public class ProgrammableSwitchCompat extends SwitchCompat {

    public boolean isCheckedProgrammatically = false;

    public ProgrammableSwitchCompat(final Context context) {
        super(context);
    }

    public ProgrammableSwitchCompat(final Context context, final AttributeSet attrs) {
        super(context, attrs);
    }

    public ProgrammableSwitchCompat(final Context context, final AttributeSet attrs, final int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void setChecked(boolean checked) {
        isCheckedProgrammatically = false;
        super.setChecked(checked);
    }

    public void setCheckedProgrammatically(boolean checked) {
        isCheckedProgrammatically = true;
        super.setChecked(checked);
    }
}

use it

@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean on) {
    if (((ProgrammableSwitchCompat) compoundButton).isCheckedProgrammatically) {
        return;
    }
    //...
    ((ProgrammableSwitchCompat) compoundButton).setCheckedProgrammatically(true);
    //...
    ((ProgrammableSwitchCompat) compoundButton).setCheckedProgrammatically(false);
    //...
}

use will trigger setChecked(boolean) function
that is all

KOTLIN

class MyCheckBox @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = R.attr.switchStyle
) : AppCompatCheckBox(context, attrs, defStyleAttr) {

    var programmatically = false

    override fun setChecked(checked: Boolean) {
        programmatically = false
        super.setChecked(checked)
    }

    fun setCheckedProgrammatically(checked: Boolean) {
        programmatically = true
        super.setChecked(checked)
    }
}
Vlad
  • 7,997
  • 3
  • 56
  • 43
3

This is a simple solution I used:
Define a custom listener:

class CompoundButtonListener implements CompoundButton.OnCheckedChangeListener {

    boolean enabled = false;

    @Override
    public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {

    }

    void enable() {
        enabled = true;
    }

    void disable() {
        enabled = false;
    }

    boolean isEnabled() {
        return enabled;
    }
}

Initialization:

CompoundButtonListener checkBoxListener = new CompoundButtonListener() {
    @Override
    public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
        if (isEnabled()) {
            // Your code goes here
        }
    }
};
myCheckBox.setOnCheckedChangeListener(checkBoxListener);

Usage:

checkBoxListener.disable();

// Some logic based on which you will modify CheckBox state
// Example: myCheckBox.setChecked(true)

checkBoxListener.enable();
vovahost
  • 34,185
  • 17
  • 113
  • 116
2

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
            }

        }
    });
areema
  • 241
  • 2
  • 3
2

Try this one should work for you! You can use this with firebase also!

For get firebase data! Use this!

databaseReference.child(user.getPhoneNumber()).child("Reqs").addValueEventListener(new ValueEventListener() {

        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            SharedPreferences prefs = mContext.getSharedPreferences("uinfo", MODE_PRIVATE);
            String pno = prefs.getString("username", "No name defined");

            if(dataSnapshot.child(pno).getValue(String.class).equals("acc")){
                holder.acc.setChecked(true);
            }else{
                holder.acc.setChecked(false);
            }
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {
            // Getting Post failed, log a message
            Log.w("dfs", "loadPost:onCancelled", databaseError.toException());
            // ...
        }
    });

After that when user do something!

holder.acc.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if(isChecked) {
                    if(buttonView.isPressed()) {
                        //your code
                    }
                }
                else {
                    if(buttonView.isPressed()) {
                       //your code
                    }
                }
            }
        });
Lakpriya Senevirathna
  • 2,488
  • 2
  • 17
  • 35
1

I found all the above answers way too complicated. Why not just create your own flag with a simple boolean?

Just use a simple flag system with a boolean. Create boolean noListener. Whenever you want to turn your switch on/off without running any code (in this example, represented as runListenerCode(), simply set noListener=true before calling switch.setChecked(false/true)

switch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
           @Override
           public void onCheckedChanged(CompoundButton compoundButton, boolean selected) {
               if (!noListener) { //If we want to run our code like usual
                   runListenerCode();
               } else { //If we simply want the switch to turn off
                   noListener = false;
               }
           });

Very simple solution using simple flags. At the end, we set noListener=false once again so that our code continues to work. Hope this helps!

Ruchir Baronia
  • 7,406
  • 5
  • 48
  • 83
1

I used a ReentrantLock, and lock it whenever I'm setting isChecked:

Kotlin:

// lock when isChecked is being set programmatically
val isBeingProgrammaticallySet = ReentrantLock()

// set isChecked programmatically
isBeingProgrammaticallySet.withLock()
{
    checkbox.isChecked = true
}

// do something only when preference is modified by user
checkbox.setOnCheckedChangeListener()
{
    _,isChecked ->
    if (isBeingProgrammaticallySet.isHeldByCurrentThread.not())
    {
        // do it
    }
}
Eric
  • 16,397
  • 8
  • 68
  • 76
1

Here's a version of the tag technique that is very easy to use.

Usage:

// Pass true to enable bypassing of the listener
button.setOnCheckedChangedListener(true) { _, isChecked ->
    // your usual code
}

// Use extension function to set the value and bypass the listener
button.setCheckedSilently(true)

It's done with a couple of utility extension functions:

inline fun CompoundButton.setOnCheckedChangeListener(canBypass: Boolean, crossinline listener: (CompoundButton, Boolean) -> Unit) {
    if (canBypass) {
        setOnCheckedChangeListener { view, isChecked ->
            if (view.tag != ListenerBypass) {
                listener(view, isChecked)
            }
        }
    } else {
        setOnCheckedChangeListener { view, isChecked -> listener(view, isChecked) }
    }
}

fun CompoundButton.setCheckedSilently(isChecked: Boolean) {
    val previousTag = tag
    tag = ListenerBypass
    this.isChecked = isChecked
    tag = previousTag
}

object ListenerBypass
Tenfour04
  • 83,111
  • 11
  • 94
  • 154
1

isPressed() checked once in onCreate(). add checkbox.isPressed() after your onCheckChangeListener to check every time your check box change.

amirali
  • 11
  • 3
0

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();
}
Zielony
  • 16,239
  • 6
  • 34
  • 39
  • 1
    May work until devs change field name or, e.g., pull up "isChecked" method in hierarchy... or do another refactoring... At least add something like `if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN){ /* do reflection */}` – æ-ra-code Feb 12 '15 at 13:44
  • 1
    It's a wrong idea to encourage to break the API and dig into internals. Any change to the implementation will cause apps to fail. – mspanc Oct 28 '15 at 17:49
0

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.

Makalele
  • 7,431
  • 5
  • 54
  • 81
0

I didn't really want to be having to pass the listener in each time we set checked changed, nor using enabled as a way of determining whether we should set the value (what happens in the case we have the switch disabled already when setting the value?)

Instead I'm making use of tags with an id and a couple of extension methods you can call:

fun CompoundButton.setOnCheckedWithoutCallingChangeListener(
    listener: (view: CompoundButton, checked: Boolean) -> Unit
) {
    setOnCheckedChangeListener { view, checked ->
        if (view.getTag(R.id.compound_button_checked_changed_listener_disabled) != true) {
            listener(view, checked)
        }
    }
    this.setTag(R.id.compound_button_enabled_checked_change_supported, true)
}

fun CompoundButton.setCheckedWithoutCallingListener(checked: Boolean) {
    check(this.getTag(R.id.compound_button_enabled_checked_change_supported) == true) {
        "Must set listener using `setOnCheckedWithoutCallingChangeListener` to call this method" 
    }

    setTag(R.id.compound_button_checked_changed_listener_disabled, true)
    isChecked = checked
    setTag(R.id.compound_button_checked_changed_listener_disabled, false)
}

Now you can call setCheckedWithoutCallingListener(bool) and it will enforce the correct listener usage.

You can also still call setChecked(bool) to fire the listener if you still need it

daentech
  • 1,125
  • 12
  • 16
0

I think this could resolve your problem.

Extension function

fun CheckBox.setSilentCheck(isCheck:Boolean,listener:CompoundButton.OnCheckedChangeListener) {
setOnCheckedChangeListener(null)
isChecked = isCheck
setOnCheckedChangeListener(listener) }