Cambie el valor de la casilla de verificación sin activar onCheckChanged

147

He setOnCheckedChangeListenerimplementado para micheckbox

¿Hay alguna manera de llamar?

checkbox.setChecked(false);

sin activar el onCheckedChanged

Archie.bpgc
fuente
¿Por qué no usar una simple bandera de verdadero / falso? Es la forma más sencilla de solucionar este problema y solo se necesitan como tres líneas de código adicional. Vea mi respuesta a continuación.
Ruchir Baronia

Respuestas:

280

No, no puedes hacerlo. El onCheckedChangedmétodo se llama directamente desde setChecked. Lo que puedes hacer es lo siguiente:

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

Vea la fuente de CheckBox y la implementación de 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;            
    }
}
Sombra
fuente
66
¿Cómo te propones llegar mListener? Checkboxno tiene un captador para suOnCheckChangeListener
tir38
20
Bueno, no hay necesidad de votar negativamente simplemente porque no entiendes la solución. mListeneres una implementación de la OnCheckChangedListenerinterfaz, que fue creada por el programador. Mi respuesta implica que el programador mantiene una referencia a su propia aplicación - mListener.
Sombra
¿Sería ineficiente cambiar el oyente si desea utilizar el método setChecked () de forma repetitiva?
Ren
44
@Ren, cambiar el oyente implica solo la configuración de una propiedad en el CheckBoxobjeto. No diría que es ineficiente.
Sombra
El documento dice "Llamado cuando el botón de radio marcado ha cambiado. Cuando se borra la selección, checkId es -1". Esto es realmente engañoso, ya sea que también se haga pasar el isChecked.
AA_PV
69

Agregue este código dentro de OnCheckedChangeListener:

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

Esto nos ayudará a descubrir que el estado de la casilla de verificación del clima se modificó mediante programación o por acción del usuario.

krisDrOid
fuente
11
Esto debe ser marcado como una solución, ¡funciona como un encanto!
Ashwin Balani
55
ten cuidado! ¡Esto rompe el modo de accesibilidad! isPressed () no es cierto cuando se activa con un doble toque desde el modo de asistente de voz.
A1m
21

Otra posible forma de lograr esto es mediante el uso de un CheckBox personalizado, que le permitirá elegir si desea que se llame al oyente o no:

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();
    }
}

Versión de Kotlin, si lo prefiere:

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()
    }
}

muestra de uso:

checkBox.setChecked(true,false);
desarrollador de Android
fuente
11

usa simplemente setonclickListener, funcionará bien y este es un método muy simple, gracias :)

Rohit
fuente
35
onClick () no se llama cuando el usuario desliza el interruptor.
James
2
De hecho, hacer esto te deja sin controlador cuando el usuario arrastra la palanca.
Victor G
entonces, ¿cómo obtendrá el valor isChecked, es decir, verdadero o falso?
Abhi
6

Usando las extensiones de Kotlin con la respuesta @Shade:

fun CompoundButton.setCustomChecked(value: Boolean,listener: CompoundButton.OnCheckedChangeListener) {
     setOnCheckedChangeListener(null)
     isChecked = value
     setOnCheckedChangeListener(listener)
}
Mickey Mouse
fuente
6

Para cualquiera que se encuentre con esto, una forma más sencilla de hacerlo es usar una etiqueta en la casilla de verificación y luego verificar esa etiqueta en su escucha (el código está en Kotlin):

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

Luego, cuando acceda, simplemente configure la etiqueta en verdadero antes de establecer isChecked en verdadero cuando desee ignorar el cambio de valor:

checkBox.tag = true
checkBox.isChecked = true

También puede asignar la etiqueta a una clave utilizando el método alternativo setTag que requiere una clave si le preocupa la comprensión. Pero si todo está contenido en una sola clase, unas pocas cadenas de comentarios serán más que suficientes para explicar lo que está sucediendo.

Chris
fuente
4

Puede usar esta clase SafeCheckBox como su casilla de verificación:

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);
    }
}
  • Entonces puedes llamar: -

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

krishan
fuente
3

Establezca nulo en changeListener antes de verificar el botón de radio. Puede configurar el oyente nuevamente después de verificar el botón de radio.

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

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

   }
});
gencaysahinn
fuente
1
Hace el trabajo y es mucho más rápido y simple que las otras soluciones aquí.
PayToPwn
3

Mi interpretación, que creo que es la más fácil,
puede ser útil)

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);
    }
}

úsalo

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

el uso activará la setChecked(boolean)función
que es todo

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
fuente
2

Esta es una solución simple que utilicé:
definir un oyente personalizado:

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;
    }
}

Inicializacion:

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

Uso:

checkBoxListener.disable();

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

checkBoxListener.enable();
vovahost
fuente
2

Qué tal esto. Intenta usar la etiqueta en la vista

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

y

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
fuente
2

Usé un ReentrantLocky lo bloqueo cada vez que configuro isChecked:

// 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
fuente
1

Encontré todas las respuestas anteriores demasiado complicadas. ¿Por qué no simplemente crear su propia bandera con un simple booleano?

Simplemente use un sistema de bandera simple con un booleano. Crear boolean noListener. Siempre que desee encender / apagar su interruptor sin ejecutar ningún código (en este ejemplo, representado como runListenerCode(), simplemente configúrelo noListener=trueantes de llamarswitch.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;
               }
           });

Solución muy simple usando banderas simples. Al final, configuramos noListener=falseuna vez más para que nuestro código continúe funcionando. ¡Espero que esto ayude!

Ruchir Baronia
fuente
1

¡Prueba que este debería funcionar para ti! ¡Puedes usar esto con firebase también!

¡Para obtener datos de firebase! ¡Utilizar este!

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());
            // ...
        }
    });

Después de eso, cuando el usuario haga algo!

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
fuente
1

Realmente no quería tener que pasar al oyente cada vez que configuramos el cambio modificado, ni usarlo enabledcomo una forma de determinar si debemos establecer el valor (lo que sucede en el caso de que ya tengamos el interruptor desactivado al establecer el valor ?)

En cambio, estoy usando etiquetas con una identificación y un par de métodos de extensión a los que puede llamar:

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)
}

Ahora puede llamar setCheckedWithoutCallingListener(bool)y exigirá el uso correcto del oyente.

También puede llamar setChecked(bool)para despedir al oyente si aún lo necesita

Daentech
fuente
0

Supongo que usar la reflexión es la única forma. Algo como esto:

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
fuente
Puede funcionar hasta que los desarrolladores cambien el nombre del campo o, por ejemplo, activen el método "isChecked" en la jerarquía ... o realicen otra refactorización ... Al menos agregue algo comoif(Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN){ /* do reflection */}
aeracode
Es una idea equivocada alentar a romper la API y profundizar en lo interno. Cualquier cambio en la implementación hará que las aplicaciones fallen.
mspanc
0

Mi solución escrita en Java basada en @Chris responde:

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 casillas de verificación y siempre una estará marcada (aunque una debe estar marcada inicialmente). Establecer la etiqueta en bloques verdaderos en el oyenteCheckedChanged.

Makalele
fuente
0
public void setCheckedChangeListenerSwitch() {

    switch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton compoundButton, boolean selected) {

            if (selected) {
                // do something
            } else {
                // do something else
            }

        }
    });

}

// Update state & reset listener (so onCheckedNot called)
public void updateSwitchState(boolean state){

    switch.setOnCheckedChangeListener(null);
    switch.setChecked(state);
    setCheckedChangeListenerSwitch();

}
codebyjames
fuente