Warum nicht einfach ein einfaches True / False-Flag verwenden? Dies ist der einfachste Weg, um dieses Problem zu lösen, und es werden nur drei Zeilen zusätzlichen Codes benötigt. Siehe meine Antwort unten.
Ruchir Baronia
Antworten:
279
Nein, das kannst du nicht. Die onCheckedChangedMethode wird direkt von aufgerufen setChecked. Was Sie tun können, ist Folgendes:
Wie schlagen Sie vor, zu bekommen mListener? Checkboxhat keinen Getter für seineOnCheckChangeListener
tir38
20
Sie müssen nicht einfach abstimmen, weil Sie die Lösung nicht verstehen. mListenerist eine Implementierung der OnCheckChangedListenerSchnittstelle, die vom Programmierer erstellt wurde. Meine Antwort impliziert, dass der Programmierer einen Verweis auf seine eigene Implementierung beibehalten hat - mListener.
Schatten
Wäre es ineffizient, den Listener zu ändern, wenn Sie die setChecked () -Methode wiederholt verwenden möchten?
Ren
4
@Ren, das Ändern des Listeners beinhaltet nur das Festlegen einer Eigenschaft im CheckBoxObjekt. Ich würde nicht sagen, dass das ineffizient ist.
Schatten
Das Dokument lautet "Wird aufgerufen, wenn sich das aktivierte Optionsfeld geändert hat. Wenn die Auswahl gelöscht ist, ist" CheckedId "-1". Dies ist wirklich irreführend. Entweder sollte auch das isChecked durchlaufen werden.
AA_PV
69
Fügen Sie diesen Code in OnCheckedChangeListener hinzu:
if(!compoundButton.isPressed()){return;}
Dies hilft uns herauszufinden, ob der Status der Wetterkontrollbox programmgesteuert oder durch Benutzeraktion geändert wurde.
Dies sollte als Lösung markiert werden, funktioniert wie ein Zauber!
Ashwin Balani
5
sei vorsichtig! Dies unterbricht den Eingabehilfenmodus! isPressed () ist nicht wahr, wenn es durch zweimaliges Tippen aus dem Sprachassistentenmodus ausgelöst wird.
A1m
21
Eine andere Möglichkeit, dies zu erreichen, ist die Verwendung einer benutzerdefinierten CheckBox, mit der Sie auswählen können, ob der Listener aufgerufen werden soll oder nicht:
Für alle, die darauf stoßen, besteht eine einfachere Möglichkeit darin, einfach ein Tag im Kontrollkästchen zu verwenden und dieses Tag dann im Listener zu aktivieren (Code befindet sich in Kotlin):
Setzen Sie dann beim Zugriff einfach das Tag auf true, bevor Sie isChecked auf true setzen, wenn Sie die Wertänderung ignorieren möchten:
checkBox.tag =true
checkBox.isChecked =true
Sie können das Tag auch einem Schlüssel zuordnen, indem Sie die alternative setTag-Methode verwenden, für die ein Schlüssel erforderlich ist, wenn Sie Bedenken hinsichtlich der Verständlichkeit haben. Aber wenn alles in einer einzelnen Klasse enthalten ist, sind ein paar Kommentarzeichenfolgen mehr als genug, um zu erklären, was passiert.
Sie können diese SafeCheckBox-Klasse als Kontrollkästchen verwenden:
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);}}
Dann könnten Sie anrufen: -
setSafeCheck(true,ListenerMode.IGNORE);// OnCheckedChange listener will not be notified
Setzen Sie null auf changeListener, bevor Sie das Optionsfeld aktivieren. Sie können den Listener erneut einstellen, nachdem Sie das Optionsfeld aktiviert haben.
CompoundButtonListener checkBoxListener =newCompoundButtonListener(){@Overridepublicvoid onCheckedChanged(CompoundButton compoundButton,booleanchecked){if(isEnabled()){// Your code goes here}}};
myCheckBox.setOnCheckedChangeListener(checkBoxListener);
Verwendung:
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}}});
Ich habe a verwendet ReentrantLockund es gesperrt, wenn ich Folgendes einstelle 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}}
Ich fand alle obigen Antworten viel zu kompliziert. Warum erstellen Sie nicht einfach Ihre eigene Flagge mit einem einfachen Booleschen Wert?
Verwenden Sie einfach ein einfaches Flag-System mit einem Booleschen Wert. Erstellen boolean noListener. Wann immer Sie Ihren Schalter ein- und ausschalten möchten, ohne Code auszuführen (in diesem Beispiel dargestellt als runListenerCode(), einfach noListener=truevor dem Aufruf festgelegtswitch.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;}});
Sehr einfache Lösung mit einfachen Flags. Am Ende setzen wir noListener=falsenoch einmal, damit unser Code weiter funktioniert. Hoffe das hilft!
Ich wollte nicht wirklich den Listener jedes Mal übergeben müssen, wenn wir das Kontrollkästchen geändert oder verwendet haben enabled , um zu bestimmen, ob wir den Wert einstellen sollen (was passiert, wenn der Schalter bereits beim Einstellen des Werts deaktiviert ist ?)
Stattdessen verwende ich Tags mit einer ID und einigen Erweiterungsmethoden, die Sie aufrufen können:
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)}
Jetzt können Sie anrufen setCheckedWithoutCallingListener(bool)und die korrekte Verwendung des Listeners erzwingen.
Sie können auch weiterhin anrufen setChecked(bool), um den Listener zu feuern, wenn Sie ihn noch benötigen
Kann funktionieren, bis Entwickler den Feldnamen ändern oder z. B. die "isChecked" -Methode in der Hierarchie aufrufen ... oder ein anderes Refactoring durchführen ... if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN){ /* do reflection */}
Fügen Sie
Es ist eine falsche Idee, zu ermutigen, die API zu brechen und sich mit Interna zu befassen. Jede Änderung an der Implementierung führt dazu, dass Apps fehlschlagen.
mspanc
0
Meine in Java geschriebene Lösung basiert auf der Antwort von @Chris:
2 Kontrollkästchen und immer eines wird aktiviert (eines muss jedoch zunächst aktiviert werden). Setzen des Tags auf true blockiert onCheckedChanged Listener.
Antworten:
Nein, das kannst du nicht. Die
onCheckedChanged
Methode wird direkt von aufgerufensetChecked
. Was Sie tun können, ist Folgendes:Siehe die Quelle von CheckBox und die Implementierung von
setChecked
:quelle
mListener
?Checkbox
hat keinen Getter für seineOnCheckChangeListener
mListener
ist eine Implementierung derOnCheckChangedListener
Schnittstelle, die vom Programmierer erstellt wurde. Meine Antwort impliziert, dass der Programmierer einen Verweis auf seine eigene Implementierung beibehalten hat -mListener
.CheckBox
Objekt. Ich würde nicht sagen, dass das ineffizient ist.Fügen Sie diesen Code in OnCheckedChangeListener hinzu:
Dies hilft uns herauszufinden, ob der Status der Wetterkontrollbox programmgesteuert oder durch Benutzeraktion geändert wurde.
quelle
Eine andere Möglichkeit, dies zu erreichen, ist die Verwendung einer benutzerdefinierten CheckBox, mit der Sie auswählen können, ob der Listener aufgerufen werden soll oder nicht:
Kotlin-Version, wenn Sie es vorziehen:
Beispielnutzung:
quelle
Sie verwenden einfach setonclickListener, es wird gut funktionieren und dies ist eine sehr einfache Methode, danke :)
quelle
Verwenden von Kotlins Erweiterungen mit @Shade Antwort:
quelle
Für alle, die darauf stoßen, besteht eine einfachere Möglichkeit darin, einfach ein Tag im Kontrollkästchen zu verwenden und dieses Tag dann im Listener zu aktivieren (Code befindet sich in Kotlin):
Setzen Sie dann beim Zugriff einfach das Tag auf true, bevor Sie isChecked auf true setzen, wenn Sie die Wertänderung ignorieren möchten:
Sie können das Tag auch einem Schlüssel zuordnen, indem Sie die alternative setTag-Methode verwenden, für die ein Schlüssel erforderlich ist, wenn Sie Bedenken hinsichtlich der Verständlichkeit haben. Aber wenn alles in einer einzelnen Klasse enthalten ist, sind ein paar Kommentarzeichenfolgen mehr als genug, um zu erklären, was passiert.
quelle
Sie können diese SafeCheckBox-Klasse als Kontrollkästchen verwenden:
Dann könnten Sie anrufen: -
setSafeCheck(true,ListenerMode.IGNORE);// OnCheckedChange listener will not be notified
quelle
Setzen Sie null auf changeListener, bevor Sie das Optionsfeld aktivieren. Sie können den Listener erneut einstellen, nachdem Sie das Optionsfeld aktiviert haben.
quelle
Meine Interpretation, die ich für die einfachste halte,
kann hilfreich sein.
benutze es
Mit use wird eine
setChecked(boolean)
Funktion ausgelöst, die alles ist
KOTLIN
quelle
Dies ist eine einfache Lösung, die ich verwendet habe:
Definieren Sie einen benutzerdefinierten Listener:
Initialisierung:
Verwendung:
quelle
Wie wäre es damit. Versuchen Sie, Tag in View zu verwenden
und
quelle
Ich habe a verwendet
ReentrantLock
und es gesperrt, wenn ich Folgendes einstelleisChecked
:quelle
Verwenden Sie einfach ein einfaches Flag-System mit einem Booleschen Wert. Erstellen
boolean noListener
. Wann immer Sie Ihren Schalter ein- und ausschalten möchten, ohne Code auszuführen (in diesem Beispiel dargestellt alsrunListenerCode()
, einfachnoListener=true
vor dem Aufruf festgelegtswitch.setChecked(false/true)
Sehr einfache Lösung mit einfachen Flags. Am Ende setzen wir
noListener=false
noch einmal, damit unser Code weiter funktioniert. Hoffe das hilft!quelle
Versuchen Sie dieses sollte für Sie arbeiten! Sie können dies auch mit Firebase verwenden!
Um Firebase-Daten zu erhalten! Benutze das!
Danach, wenn Benutzer etwas tun!
quelle
Ich wollte nicht wirklich den Listener jedes Mal übergeben müssen, wenn wir das Kontrollkästchen geändert oder verwendet haben
enabled
, um zu bestimmen, ob wir den Wert einstellen sollen (was passiert, wenn der Schalter bereits beim Einstellen des Werts deaktiviert ist ?)Stattdessen verwende ich Tags mit einer ID und einigen Erweiterungsmethoden, die Sie aufrufen können:
Jetzt können Sie anrufen
setCheckedWithoutCallingListener(bool)
und die korrekte Verwendung des Listeners erzwingen.Sie können auch weiterhin anrufen
setChecked(bool)
, um den Listener zu feuern, wenn Sie ihn noch benötigenquelle
Ich denke, Reflexion ist der einzige Weg. Etwas wie das:
quelle
if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN){ /* do reflection */}
Meine in Java geschriebene Lösung basiert auf der Antwort von @Chris:
2 Kontrollkästchen und immer eines wird aktiviert (eines muss jedoch zunächst aktiviert werden). Setzen des Tags auf true blockiert onCheckedChanged Listener.
quelle
quelle