Pourquoi ne pas simplement utiliser un simple drapeau vrai / faux? C'est le moyen le plus simple de résoudre ce problème et cela ne prend que trois lignes de code supplémentaires. Voir ma réponse ci-dessous.
Ruchir Baronia
Réponses:
280
Non, tu ne peux pas le faire. La onCheckedChangedméthode est appelée directement à partir de setChecked. Voici ce que vous pouvez faire:
Comment proposez-vous d'obtenir mListener? Checkboxn'a pas de getter pour sonOnCheckChangeListener
tir38
20
Eh bien, pas besoin de voter simplement parce que vous ne comprenez pas la solution. mListenerest une implémentation de l' OnCheckChangedListenerinterface, qui a été créée par le programmeur. Ma réponse implique que le programmeur a maintenu une référence à sa propre implémentation - mListener.
Ombre le
Serait-il inefficace de changer l'écouteur si vous souhaitez utiliser la méthode setChecked () de manière répétitive?
Ren
4
@Ren, la modification de l'écouteur implique uniquement la définition d'une propriété dans l' CheckBoxobjet. Je ne dirais pas que c'est inefficace.
Ombre le
Le document lit "Appelé lorsque le bouton radio coché a changé. Lorsque la sélection est effacée, vérifiéId est -1". Ceci est vraiment trompeur, il devrait également avoir le isChecked.
AA_PV
69
Ajoutez ce code dans OnCheckedChangeListener:
if(!compoundButton.isPressed()){return;}
Cela nous aidera à comprendre que l'état de la case à cocher météo a été modifié par programme ou par action de l'utilisateur.
Cela devrait être marqué comme une solution, fonctionne comme un charme!
Ashwin Balani
5
être conscient! Cela rompt le mode d'accessibilité! isPressed () n'est pas vrai lorsqu'il est déclenché par une double pression depuis le mode assistant vocal.
A1m
21
Un autre moyen possible d'y parvenir est d'utiliser une case à cocher personnalisée, qui vous permettra de choisir si vous voulez que l'écouteur soit appelé ou non:
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()}}
Pour tous ceux qui trébuchent sur cela, un moyen plus simple de le faire est d'utiliser simplement une balise sur la case à cocher, puis de vérifier cette balise sur son auditeur (le code est en Kotlin):
Ensuite, lors de l'accès, définissez simplement la balise sur true avant de définir isChecked sur true lorsque vous souhaitez ignorer le changement de valeur:
checkBox.tag =true
checkBox.isChecked =true
Vous pouvez également mapper la balise à une clé en utilisant la méthode alternative setTag qui nécessite une clé si vous vous inquiétez de la compréhensibilité. Mais si tout est contenu dans une seule classe, quelques chaînes de commentaires seront plus que suffisantes pour expliquer ce qui se passe.
Vous pouvez utiliser cette classe SafeCheckBox comme case à cocher:
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);}}
Ensuite, vous pouvez appeler: -
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);
Usage:
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}}});
J'ai utilisé un ReentrantLock, et je le verrouille chaque fois que je règle 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}}
J'ai trouvé toutes les réponses ci-dessus bien trop compliquées. Pourquoi ne pas simplement créer votre propre drapeau avec un simple booléen?
Utilisez simplement un système d'indicateur simple avec un booléen. Créez boolean noListener. Chaque fois que vous souhaitez activer / désactiver votre interrupteur sans exécuter de code (dans cet exemple, représenté par runListenerCode(), il suffit de définir noListener=trueavant d'appelerswitch.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;}});
Solution très simple utilisant des drapeaux simples. À la fin, nous réglons noListener=falseà nouveau pour que notre code continue de fonctionner. J'espère que cela t'aides!
Je ne voulais pas vraiment avoir à passer l'auditeur à chaque fois que nous définissons coché changé, ni utiliser enabledcomme moyen de déterminer si nous devons définir la valeur (que se passe-t-il dans le cas où le commutateur est déjà désactivé lors de la définition de la valeur ?)
Au lieu de cela, j'utilise des balises avec un identifiant et quelques méthodes d'extension que vous pouvez appeler:
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)}
Peut fonctionner jusqu'à ce que les développeurs changent le nom du champ ou, par exemple, tirent la méthode "isChecked" dans la hiérarchie ... ou font une autre refactorisation ... Au moins ajouter quelque chose commeif(Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN){ /* do reflection */}
aeracode
C'est une mauvaise idée d'encourager à casser l'API et à creuser dans les internes. Toute modification de l'implémentation entraînera l'échec des applications.
mspanc
0
Ma solution écrite en java basée sur la réponse @Chris:
Réponses:
Non, tu ne peux pas le faire. La
onCheckedChanged
méthode est appelée directement à partir desetChecked
. Voici ce que vous pouvez faire:Voir la source de CheckBox et l'implémentation de
setChecked
:la source
mListener
?Checkbox
n'a pas de getter pour sonOnCheckChangeListener
mListener
est une implémentation de l'OnCheckChangedListener
interface, qui a été créée par le programmeur. Ma réponse implique que le programmeur a maintenu une référence à sa propre implémentation -mListener
.CheckBox
objet. Je ne dirais pas que c'est inefficace.Ajoutez ce code dans OnCheckedChangeListener:
Cela nous aidera à comprendre que l'état de la case à cocher météo a été modifié par programme ou par action de l'utilisateur.
la source
Un autre moyen possible d'y parvenir est d'utiliser une case à cocher personnalisée, qui vous permettra de choisir si vous voulez que l'écouteur soit appelé ou non:
Version Kotlin, si vous préférez:
utilisation de l'échantillon:
la source
vous utilisez simplement setonclickListener, cela fonctionnera très bien et c'est une méthode très simple, merci :)
la source
Utilisation des extensions de Kotlin avec la réponse @Shade:
la source
Pour tous ceux qui trébuchent sur cela, un moyen plus simple de le faire est d'utiliser simplement une balise sur la case à cocher, puis de vérifier cette balise sur son auditeur (le code est en Kotlin):
Ensuite, lors de l'accès, définissez simplement la balise sur true avant de définir isChecked sur true lorsque vous souhaitez ignorer le changement de valeur:
Vous pouvez également mapper la balise à une clé en utilisant la méthode alternative setTag qui nécessite une clé si vous vous inquiétez de la compréhensibilité. Mais si tout est contenu dans une seule classe, quelques chaînes de commentaires seront plus que suffisantes pour expliquer ce qui se passe.
la source
Vous pouvez utiliser cette classe SafeCheckBox comme case à cocher:
Ensuite, vous pouvez appeler: -
setSafeCheck(true,ListenerMode.IGNORE);// OnCheckedChange listener will not be notified
la source
Définissez null sur changeListener avant de cocher le bouton radio. Vous pouvez définir à nouveau l'auditeur après avoir coché le bouton radio.
la source
Mon interprétation que je pense être la plus simple
peut être utile)
utilise le
l'utilisation déclenchera la
setChecked(boolean)
fonctionqui est tout
KOTLIN
la source
C'est une solution simple que j'ai utilisée:
définir un écouteur personnalisé:
Initialisation:
Usage:
la source
Que dis-tu de ça. Essayez d'utiliser la balise dans la vue
et
la source
J'ai utilisé un
ReentrantLock
, et je le verrouille chaque fois que je règleisChecked
:la source
Utilisez simplement un système d'indicateur simple avec un booléen. Créez
boolean noListener
. Chaque fois que vous souhaitez activer / désactiver votre interrupteur sans exécuter de code (dans cet exemple, représenté parrunListenerCode()
, il suffit de définirnoListener=true
avant d'appelerswitch.setChecked(false/true)
Solution très simple utilisant des drapeaux simples. À la fin, nous réglons
noListener=false
à nouveau pour que notre code continue de fonctionner. J'espère que cela t'aides!la source
Essayez celui-ci devrait fonctionner pour vous! Vous pouvez également l'utiliser avec Firebase!
Pour obtenir des données Firebase! Utilisez ceci!
Après cela, lorsque l'utilisateur fait quelque chose!
la source
Je ne voulais pas vraiment avoir à passer l'auditeur à chaque fois que nous définissons coché changé, ni utiliser
enabled
comme moyen de déterminer si nous devons définir la valeur (que se passe-t-il dans le cas où le commutateur est déjà désactivé lors de la définition de la valeur ?)Au lieu de cela, j'utilise des balises avec un identifiant et quelques méthodes d'extension que vous pouvez appeler:
Vous pouvez maintenant appeler
setCheckedWithoutCallingListener(bool)
et cela imposera l'utilisation correcte de l'auditeur.Vous pouvez également appeler
setChecked(bool)
pour renvoyer l'auditeur si vous en avez encore besoinla source
Je suppose que l'utilisation de la réflexion est le seul moyen. Quelque chose comme ça:
la source
if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN){ /* do reflection */}
Ma solution écrite en java basée sur la réponse @Chris:
2 cases à cocher et toujours une sera cochée (une doit être cochée au départ). Définition de la balise sur true blocs onCheckedChanged listener.
la source
la source