Dlaczego po prostu nie użyć prostej flagi prawda / fałsz? To najprostszy sposób rozwiązania tego problemu i potrzeba tylko trzech wierszy dodatkowego kodu. Zobacz moją odpowiedź poniżej.
Ruchir Baronia
Odpowiedzi:
280
Nie, nie możesz tego zrobić. onCheckedChangedMetoda jest wywoływana bezpośrednio setChecked. Co możesz zrobić, to:
Jak proponujesz dostać mListener? Checkboxnie ma gettera dla swojegoOnCheckChangeListener
tir38
20
Cóż, nie musisz głosować „w dół” tylko dlatego, że nie rozumiesz rozwiązania. mListenerjest implementacją OnCheckChangedListenerinterfejsu, który został stworzony przez programistę. Z mojej odpowiedzi wynika, że programista zachował odniesienie do własnej implementacji - mListener.
Shade
Czy zmiana odbiornika byłaby nieefektywna, jeśli chcesz używać metody setChecked () w sposób powtarzalny?
Ren
4
@Ren, zmiana detektora obejmuje tylko ustawienie właściwości w CheckBoxobiekcie. Nie powiedziałbym, że jest to nieefektywne.
Shade
Dokument brzmi „Wywoływane, gdy zaznaczony przycisk opcji się zmienił. Po wyczyszczeniu zaznaczenia, CheckId wynosi -1”. Jest to naprawdę mylące, powinno też zostać przekazane isChecked.
AA_PV,
69
Dodaj ten kod do OnCheckedChangeListener:
if(!compoundButton.isPressed()){return;}
Pomoże nam to dowiedzieć się, czy stan pogody checkBox został zmieniony programowo lub w wyniku działania użytkownika.
To powinno być oznaczone jako rozwiązanie, działa jak urok!
Ashwin Balani
5
być świadomym! To przerywa tryb dostępności! isPressed () nie jest prawdą, jeśli jest wyzwalany przez podwójne dotknięcie z trybu asystenta głosowego.
A1m
21
Innym możliwym sposobem osiągnięcia tego jest użycie niestandardowego CheckBox, który pozwoli Ci wybrać, czy chcesz, aby słuchacz został wywołany, czy nie:
Dla każdego, kto się na to natknie, prostszym sposobem na zrobienie tego jest użycie tagu w polu wyboru, a następnie sprawdzenie tego tagu w jego odbiorniku (kod jest w Kotlinie):
Następnie podczas uzyskiwania dostępu po prostu ustaw tag na true, zanim ustawisz isChecked na true, gdy chcesz zignorować zmianę wartości:
checkBox.tag =true
checkBox.isChecked =true
Możesz również zamapować tag na klucz, używając alternatywnej metody setTag, która wymaga klucza, jeśli martwisz się o zrozumiałość. Ale jeśli wszystko jest zawarte w jednej klasie, kilka ciągów komentarzy wystarczy, aby wyjaśnić, co się dzieje.
Możesz użyć tej klasy SafeCheckBox jako pola wyboru:
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);}}
Wtedy możesz zadzwonić: -
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);
Stosowanie:
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}}});
Użyłem ReentrantLocki blokuję go za każdym razem, gdy ustawiam 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}}
Wszystkie powyższe odpowiedzi były dla mnie zbyt skomplikowane. Dlaczego po prostu nie stworzyć własnej flagi z prostą wartością logiczną?
Po prostu użyj prostego systemu flag z wartością logiczną. Utwórz boolean noListener. Ilekroć chcesz włączyć / wyłączyć przełącznik bez uruchamiania żadnego kodu (w tym przykładzie reprezentowany jako runListenerCode(), po prostu ustaw noListener=trueprzed wywołaniemswitch.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;}});
Bardzo proste rozwiązanie przy użyciu prostych flag. Na koniec ustawiliśmy się noListener=falsejeszcze raz, aby nasz kod dalej działał. Mam nadzieję że to pomoże!
Tak naprawdę nie chciałem przekazywać słuchaczowi za każdym razem, gdy ustawiliśmy zaznaczoną zmianę, ani za pomocą enabled jako sposobu na określenie, czy powinniśmy ustawić wartość (co się dzieje w przypadku, gdy mamy wyłączony przełącznik już podczas ustawiania wartości ?)
Zamiast tego używam tagów z identyfikatorem i kilku metod rozszerzających, które możesz wywołać:
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)}
Teraz możesz dzwonić, setCheckedWithoutCallingListener(bool)a to wymusi prawidłowe użycie nasłuchiwania.
Możesz również zadzwonić, setChecked(bool)aby zwolnić słuchacza, jeśli nadal tego potrzebujesz
Może działać do momentu, gdy deweloperzy zmienią nazwę pola lub np. Podciągną metodę "isChecked" w hierarchii ... lub dokonają kolejnej refaktoryzacji ... Przynajmniej dodaj coś takiegoif(Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN){ /* do reflection */}
aeracode.
Zachęcanie do łamania API i kopania we wnętrzach jest złym pomysłem. Każda zmiana implementacji spowoduje awarię aplikacji.
mspanc
0
Moje rozwiązanie napisane w java na podstawie odpowiedzi @Chris:
2 pola wyboru i zawsze jedno będzie zaznaczone (chociaż jedno musi być zaznaczone na początku). Ustawienie znacznika na prawdziwe bloki onCheckedChanged odbiornika.
Odpowiedzi:
Nie, nie możesz tego zrobić.
onCheckedChanged
Metoda jest wywoływana bezpośredniosetChecked
. Co możesz zrobić, to:Zobacz źródło CheckBox i implementację
setChecked
:źródło
mListener
?Checkbox
nie ma gettera dla swojegoOnCheckChangeListener
mListener
jest implementacjąOnCheckChangedListener
interfejsu, który został stworzony przez programistę. Z mojej odpowiedzi wynika, że programista zachował odniesienie do własnej implementacji -mListener
.CheckBox
obiekcie. Nie powiedziałbym, że jest to nieefektywne.Dodaj ten kod do OnCheckedChangeListener:
Pomoże nam to dowiedzieć się, czy stan pogody checkBox został zmieniony programowo lub w wyniku działania użytkownika.
źródło
Innym możliwym sposobem osiągnięcia tego jest użycie niestandardowego CheckBox, który pozwoli Ci wybrać, czy chcesz, aby słuchacz został wywołany, czy nie:
Wersja Kotlin, jeśli wolisz:
przykładowe użycie:
źródło
używasz po prostu setonclickListener, będzie działać dobrze i jest to bardzo prosta metoda, dzięki :)
źródło
Używanie rozszerzeń Kotlin z odpowiedzią @Shade:
źródło
Dla każdego, kto się na to natknie, prostszym sposobem na zrobienie tego jest użycie tagu w polu wyboru, a następnie sprawdzenie tego tagu w jego odbiorniku (kod jest w Kotlinie):
Następnie podczas uzyskiwania dostępu po prostu ustaw tag na true, zanim ustawisz isChecked na true, gdy chcesz zignorować zmianę wartości:
Możesz również zamapować tag na klucz, używając alternatywnej metody setTag, która wymaga klucza, jeśli martwisz się o zrozumiałość. Ale jeśli wszystko jest zawarte w jednej klasie, kilka ciągów komentarzy wystarczy, aby wyjaśnić, co się dzieje.
źródło
Możesz użyć tej klasy SafeCheckBox jako pola wyboru:
Wtedy możesz zadzwonić: -
setSafeCheck(true,ListenerMode.IGNORE);// OnCheckedChange listener will not be notified
źródło
Ustaw null na changeListener przed zaznaczeniem przycisku radiowego. Po zaznaczeniu przycisku opcji możesz ponownie ustawić odbiornik.
źródło
Moja interpretacja, która moim zdaniem jest najłatwiejsza
Może być pomocna)
Użyj tego
użycie wywoła
setChecked(boolean)
funkcję,która jest wszystkim
KOTLIN
źródło
Oto proste rozwiązanie, którego użyłem:
Zdefiniuj odbiornik niestandardowy:
Inicjalizacja:
Stosowanie:
źródło
Co powiesz na to. Spróbuj użyć tagu w widoku
i
źródło
Użyłem
ReentrantLock
i blokuję go za każdym razem, gdy ustawiamisChecked
:źródło
Po prostu użyj prostego systemu flag z wartością logiczną. Utwórz
boolean noListener
. Ilekroć chcesz włączyć / wyłączyć przełącznik bez uruchamiania żadnego kodu (w tym przykładzie reprezentowany jakorunListenerCode()
, po prostu ustawnoListener=true
przed wywołaniemswitch.setChecked(false/true)
Bardzo proste rozwiązanie przy użyciu prostych flag. Na koniec ustawiliśmy się
noListener=false
jeszcze raz, aby nasz kod dalej działał. Mam nadzieję że to pomoże!źródło
Wypróbuj ten powinien działać dla Ciebie! Możesz tego użyć również z Firebase!
Aby uzyskać dane z Firebase! Użyj tego!
Potem, gdy użytkownik coś zrobi!
źródło
Tak naprawdę nie chciałem przekazywać słuchaczowi za każdym razem, gdy ustawiliśmy zaznaczoną zmianę, ani za pomocą
enabled
jako sposobu na określenie, czy powinniśmy ustawić wartość (co się dzieje w przypadku, gdy mamy wyłączony przełącznik już podczas ustawiania wartości ?)Zamiast tego używam tagów z identyfikatorem i kilku metod rozszerzających, które możesz wywołać:
Teraz możesz dzwonić,
setCheckedWithoutCallingListener(bool)
a to wymusi prawidłowe użycie nasłuchiwania.Możesz również zadzwonić,
setChecked(bool)
aby zwolnić słuchacza, jeśli nadal tego potrzebujeszźródło
Myślę, że jedynym sposobem jest użycie refleksji. Coś takiego:
źródło
if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN){ /* do reflection */}
Moje rozwiązanie napisane w java na podstawie odpowiedzi @Chris:
2 pola wyboru i zawsze jedno będzie zaznaczone (chociaż jedno musi być zaznaczone na początku). Ustawienie znacznika na prawdziwe bloki onCheckedChanged odbiornika.
źródło
źródło