¿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:
¿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.
classCheckBox@JvmOverloadsconstructor(context:Context, attrs:AttributeSet?=null, defStyleAttr:Int=0):AppCompatCheckBox(context, attrs, defStyleAttr){privatevar listener:CompoundButton.OnCheckedChangeListener?=nulloverride fun setOnCheckedChangeListener(listener:CompoundButton.OnCheckedChangeListener?){this.listener = listenersuper.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()}}
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):
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.
Puede usar esta clase SafeCheckBox como su casilla de verificación:
publicclassSafeCheckBoxextendsAppCompatCheckBoximplementsCompoundButton.OnCheckedChangeListener{privateOnSafeCheckedListener onSafeCheckedListener;privateint mIgnoreListener = CALL_LISTENER;publicstaticfinalint IGNORE =0;publicstaticfinalint CALL_LISTENER =1;@Retention(RetentionPolicy.SOURCE)@IntDef({IGNORE, CALL_LISTENER})public@interfaceListenerMode{}publicSafeCheckBox(Context context){super(context);
init(context);}publicSafeCheckBox(Context context,AttributeSet attrs){super(context, attrs);
init(context);}publicSafeCheckBox(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
*/publicvoid setSafeCheck(boolean checkState,@ListenerModeint 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 checkthis.mIgnoreListener = mIgnoreListener;}else{this.mIgnoreListener = CALL_LISTENER;}
setChecked(checkState);}privatevoid init(Context context){
setOnCheckedChangeListener(this);}publicOnSafeCheckedListener getOnSafeCheckedListener(){return onSafeCheckedListener;}publicvoid setOnSafeCheckedListener(OnSafeCheckedListener onSafeCheckedListener){this.onSafeCheckedListener = onSafeCheckedListener;}@Overridepublicvoid onCheckedChanged(CompoundButton buttonView,boolean isChecked){if(onSafeCheckedListener !=null)
onSafeCheckedListener.onAlwaysCalledListener(buttonView, isChecked);// this has to be called before onCheckedChangeif(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. :(
*/publicinterfaceOnSafeCheckedListenerextendsOnCheckedChangeListener{void onAlwaysCalledListener(CompoundButton buttonView,boolean isChecked);}}
Entonces puedes llamar: -
setSafeCheck(true,ListenerMode.IGNORE);// OnCheckedChange listener will not be notified
CompoundButtonListener checkBoxListener =newCompoundButtonListener(){@Overridepublicvoid onCheckedChanged(CompoundButton compoundButton,booleanchecked){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();
switch.setOnCheckedChangeListener(newCompoundButton.OnCheckedChangeListener(){@Overridepublicvoid onCheckedChanged(CompoundButton compoundButton,boolean selected){//If switch has a tag, ignore belowif(compoundButton.getTag()!=null)return;if(selected){// do something}else{// do something else}}});
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}}
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(newCompoundButton.OnCheckedChangeListener(){@Overridepublicvoid 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!
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
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:
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.
Respuestas:
No, no puedes hacerlo. El
onCheckedChanged
método se llama directamente desdesetChecked
. Lo que puedes hacer es lo siguiente:Vea la fuente de CheckBox y la implementación de
setChecked
:fuente
mListener
?Checkbox
no tiene un captador para suOnCheckChangeListener
mListener
es una implementación de laOnCheckChangedListener
interfaz, que fue creada por el programador. Mi respuesta implica que el programador mantiene una referencia a su propia aplicación -mListener
.CheckBox
objeto. No diría que es ineficiente.Agregue este código dentro de OnCheckedChangeListener:
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.
fuente
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:
Versión de Kotlin, si lo prefiere:
muestra de uso:
fuente
usa simplemente setonclickListener, funcionará bien y este es un método muy simple, gracias :)
fuente
Usando las extensiones de Kotlin con la respuesta @Shade:
fuente
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):
Luego, cuando acceda, simplemente configure la etiqueta en verdadero antes de establecer isChecked en verdadero cuando desee ignorar el cambio de valor:
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.
fuente
Puede usar esta clase SafeCheckBox como su casilla de verificación:
Entonces puedes llamar: -
setSafeCheck(true,ListenerMode.IGNORE);// OnCheckedChange listener will not be notified
fuente
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.
fuente
Mi interpretación, que creo que es la más fácil,
puede ser útil)
úsalo
el uso activará la
setChecked(boolean)
funciónque es todo
KOTLIN
fuente
Esta es una solución simple que utilicé:
definir un oyente personalizado:
Inicializacion:
Uso:
fuente
Qué tal esto. Intenta usar la etiqueta en la vista
y
fuente
Usé un
ReentrantLock
y lo bloqueo cada vez que configuroisChecked
:fuente
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 comorunListenerCode()
, simplemente configúrelonoListener=true
antes de llamarswitch.setChecked(false/true)
Solución muy simple usando banderas simples. Al final, configuramos
noListener=false
una vez más para que nuestro código continúe funcionando. ¡Espero que esto ayude!fuente
¡Prueba que este debería funcionar para ti! ¡Puedes usar esto con firebase también!
¡Para obtener datos de firebase! ¡Utilizar este!
Después de eso, cuando el usuario haga algo!
fuente
Realmente no quería tener que pasar al oyente cada vez que configuramos el cambio modificado, ni usarlo
enabled
como 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:
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 necesitafuente
Supongo que usar la reflexión es la única forma. Algo como esto:
fuente
if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN){ /* do reflection */}
Mi solución escrita en Java basada en @Chris responde:
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.
fuente
fuente