onCheckedChanged called automatically
Asked Answered
R

10

89

I have a switch in a recyclerview and data is displayed in the recyclerview after retrieving data from DB. When the recyclerview is opened I read DB and if a field in DB is "Y" I enable the switch or else I disable the switch. Now the problem is along with it the onCheckedchanged listener is also called, I want the onCheckedChanged to be called only when user sets the switch manually.

On opening the recyclerview below is executed:

holder.enabledisable.setChecked(messengerRecord.get_is_valid().equalsIgnoreCase("Y"));

ViewHolder class:

public class viewHolder extends RecyclerView.ViewHolder implements CompoundButton.OnCheckedChangeListener{
public SwitchCompat enabledisable;
 public viewHolder(View v) {
            enabledisable = (SwitchCompat) v.findViewById(R.id.enabledisable);
            enabledisable.setOnCheckedChangeListener(this);
...................................
...................................

OncheckedChanged method which is called when the recyclerView is just opened:

@Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            Log.v("ranjith","called oncheckedchanged");
            MessengerRecord rec;
            rec = dbHelper.getRecord(descview.getText().toString());
            switch (buttonView.getId()) {
                case R.id.enabledisable:
                    if (isChecked) {
                        rec.set_is_valid("Y");
                        dbHelper.updateRecord(rec);
                     }
}

In Layout file:

<android.support.v7.widget.SwitchCompat
    android:layout_marginRight="16dp"
    android:layout_marginEnd="16dp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:focusable="false"
    android:id="@+id/enabledisable"
    android:layout_alignRight="@+id/textview_to"
    android:layout_alignEnd="@+id/textview_to"
    android:layout_alignParentRight="true"
    android:layout_alignParentEnd="true"/>
Rioux answered 24/12, 2014 at 20:2 Comment(0)
C
281

It's weird all of us have this problem but not official Google answer to this simple problem.

The MOST simple it's to check:

buttonView.isPressed()

If true, means the user clicked the view.

No global variables are needed.

Caundra answered 29/1, 2015 at 16:13 Comment(6)
This is definitely working. But is there any event if the user slides the switch? In that case, If the used is sliding the switch, this is not working.Elisavetgrad
Thanks for this simple yet great solution. This can be used with RadioButton as well. For RadioGroup, I have written this answer.Incubation
Thank's @Surfian for the mention :), i'm glad this works too for RadioButtonCaundra
the solution doesn't work if you swipe the Switch very quickly. Stupid GooglePyonephritis
You can handle manually by implementing OnClickListener and checking with isChecked() method of SwitchCompat, didn't require using onCheckedChangeListener.Mccue
yes, this is useless, if you swipe quickly it will return falseJimerson
B
15

Try this

@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

        if (buttonView.isPressed()) {
          ... //returns true, if user clicks the switch button        
        }}
Brigantine answered 28/6, 2018 at 13:40 Comment(2)
This doesn't work if the swipe is done very quickly.Acrostic
Been an Android developer for over 10 years, never knew of this trick, thanks!Eleen
B
13

I've ended up using this subclass of SwitchCompat to avoid this issue. This way I don't need boilerplate code where I'm using this class. Whenever you need to change the checked without firing the listener, use setCheckedSilent instead of setChecked:

import android.content.Context;
import android.os.Parcelable;
import android.support.v7.widget.SwitchCompat;
import android.util.AttributeSet;

/**
 * Created by emanuel on 30/5/16.
 */
public class Switch extends SwitchCompat {

    private OnCheckedChangeListener listener;

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

    @Override
    public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
        this.listener = listener;
        super.setOnCheckedChangeListener(listener);
    }

    @Override
    public void onRestoreInstanceState(Parcelable state) {
        super.setOnCheckedChangeListener(null);
        super.onRestoreInstanceState(state);
        super.setOnCheckedChangeListener(listener);
    }

    public void setCheckedSilent(boolean checked) {
        super.setOnCheckedChangeListener(null);
        super.setChecked(checked);
        super.setOnCheckedChangeListener(listener);
    }
}

onRestoreInstanceState was triggering the listening also, when set in the onViewCreated method and you are going back to a previous fragment. Hope it works for you!

Brutalize answered 30/5, 2016 at 23:27 Comment(0)
G
6

Since RecyclerView is recycling views, a previously attached OnCheckedChangeListener can be triggered when setting checked value for the Switch of the new item.

When binding new data to an item:

   switch.setOnCheckedChangeListener(null) // remove any existing listener from recycled view
   switch.isChecked = [true/false] // will no longer trigger any callback to listener
   switch.setOnCheckedChangeListener { btnView, isChecked ->
       // do exiting stuff
   }
Ghat answered 15/1, 2021 at 19:57 Comment(2)
Thank you very much for posting this. I have multiple Switch views in a Recyclerview and had exactly that issue. Removing any existing listeners in onBind() at first resolved my issues.Camiecamila
you have to use holder and tag, and set listener only once, this is bad to set listeners again and again on bindJimerson
P
2

This works for me:

 boolean selected = preferences.isChecked();
        yourCheckBox.setOnCheckedChangeListener(new 
            CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, 
            boolean isChecked) {
                    if (buttonView.isPressed()) {
               preferences.setChecked(isChecked);
                    } else {
                        yourCheckBox.setChecked(selected);
                    }
                }
            });
Phenocryst answered 5/4, 2018 at 15:29 Comment(1)
This doesn't work if the swipe is done very quickly.Acrostic
P
1

Kotlin Developers:

    checkboxXYZ.setOnCheckedChangeListener { btnView, isChecked ->
        if (btnView.isPressed) {

        }
    }
Poetics answered 14/8, 2020 at 15:11 Comment(0)
B
0

use a global boolean variable and when read a data from DB set it "true" and after check(if) in onCheckChange set it "false" again. in first of onCheckChange method check if this variable is false execute codes else return(default value of this variable must be false ) ;

@Override
   public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
     if(!isSourceDB){ 

       Log.v("ranjith","called oncheckedchanged");
        MessengerRecord rec;
        rec = dbHelper.getRecord(descview.getText().toString());
        switch (buttonView.getId()) {
            case R.id.enabledisable:
                if (isChecked) {
                    rec.set_is_valid("Y");
                    dbHelper.updateRecord(rec);

          }
    }//end if
  isSourceDB = false; }// end oncheckedchange
Bontebok answered 24/12, 2014 at 20:53 Comment(2)
This seems to be a work around. I was looking for a functionality in android which calls oncheckedchanged only when clicked by user(i.e focus is recevied). But it works. Up vote from me.Rioux
@Ranjith: when Switch clicked first toggle() method is called and in toggle method setchecked() method is called. so you should insert above codes in toggle method not on checkchanged. by the way if you want just write in onCheckChange() you should write a method that only change the GUI state of switch and called it when read a data from DB.Bontebok
H
0

If you are changing state in adapter then

   holder.rawManageDriverLayoutBinding.rawManageDriverSwitch.setOnCheckedChangeListener(null);

before setOnCheckedChangeListener.

Howardhowarth answered 25/10, 2021 at 11:18 Comment(0)
T
-1

I faced the same problem inside ViewPager screen for fragments. When we switch between fragments, onCheckedChanged Listener is called again and again.

If anyone still looking for this problem, please try it.

@Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

            if (buttonView.isInTouchMode()) {
              ... //Your code will come here.         
            }
}
Tormentil answered 9/4, 2015 at 10:47 Comment(1)
it always true for isInTouchMode if it's changed automatically or manually, uselessJimerson
N
-1

Only solution is to use the method isPressed() in the listener

Nap answered 1/1, 2022 at 18:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.