Por que não usar apenas um simples sinalizador verdadeiro / falso? É a maneira mais simples de resolver esse problema e são necessárias apenas três linhas de código extra. Veja minha resposta abaixo.
Ruchir Baronia
Respostas:
280
Não, você não pode fazer isso. O onCheckedChangedmétodo é chamado diretamente de setChecked. O que você pode fazer é o seguinte:
Como você pretende receber mListener? Checkboxnão tem um getter para o seuOnCheckChangeListener
tir38 08/07/2014
20
Bem, não há necessidade de votar simplesmente porque você não entende a solução. mListeneré uma implementação da OnCheckChangedListenerinterface, criada pelo programador. Minha resposta implica que o programador manteve uma referência à sua própria implementação - mListener.
Sombra
Seria ineficiente alterar o ouvinte se você quiser usar o método setChecked () repetidamente?
Ren
4
@ Ren, alterar o ouvinte envolve apenas a configuração de uma propriedade no CheckBoxobjeto. Eu não diria que é ineficiente.
Sombra
O documento diz "Chamado quando o botão de opção marcado foi alterado. Quando a seleção é desmarcada, o ID marcado é -1". Isso é realmente enganador, pois também deve passar o isChecked.
AA_PV
69
Adicione esse código ao OnCheckedChangeListener:
if(!compoundButton.isPressed()){return;}
Isso nos ajudará a descobrir que o estado do checkBox do tempo foi alterado programaticamente ou pela ação do usuário.
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 quem se deparar com isso, uma maneira mais simples de fazer isso é usar uma tag na caixa de seleção e depois marcar essa tag no ouvinte (o código está no Kotlin):
Em seguida, ao acessar, basta definir a tag como true antes de definir isChecked como true quando desejar ignorar a alteração de valor:
checkBox.tag =true
checkBox.isChecked =true
Você também pode mapear a tag para uma chave usando o método setTag alternativo que requer uma chave se estiver preocupado com a capacidade de compreensão. Mas se tudo estiver contido em uma única classe, algumas sequências de comentários serão mais que suficientes para explicar o que está acontecendo.
Você pode usar esta classe SafeCheckBox como sua caixa de seleção:
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);}}
Então você pode ligar para: -
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}}});
Usei um ReentrantLocke bloqueio-o sempre que estou configurando 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}}
Achei todas as respostas acima muito complicadas. Por que não criar sua própria bandeira com um booleano simples?
Basta usar um sistema de sinalizador simples com um booleano. Crie boolean noListener. Sempre que você deseja ativar / desativar seu interruptor sem executar nenhum código (neste exemplo, representado como runListenerCode(), basta definir noListener=trueantes de chamarswitch.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;}});
Solução muito simples usando sinalizadores simples. No final, definimos noListener=falsemais uma vez para que nosso código continue funcionando. Espero que isto ajude!
Eu realmente não queria ter que passar pelo ouvinte a cada vez que definimos a verificação alterada, nem usando enabledcomo uma maneira de determinar se devemos definir o valor (o que acontece no caso de a chave já estar desativada ao definir o valor ?)
Em vez disso, estou usando tags com um ID e alguns métodos de extensão que você pode chamar:
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)}
Agora você pode ligar setCheckedWithoutCallingListener(bool)e aplicará o uso correto do ouvinte.
Você também pode ligar setChecked(bool)para disparar o ouvinte, se ainda precisar dele.
Pode funcionar até que o nome campo mudança devs ou, por exemplo, puxar para cima "IsChecked" método na hierarquia ... ou fazer outra refatoração ... Pelo menos adicionar algo comoif(Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN){ /* do reflection */}
aeracode
É uma idéia errada incentivar a quebra da API e a inserção de informações internas. Qualquer alteração na implementação fará com que os aplicativos falhem.
mspanc
0
Minha solução escrita em java com base na resposta @ Chris:
2 caixas de seleção e sempre uma será marcada (uma deve ser marcada inicialmente). Configurando a tag para blocos verdadeiros no ouvinte onCheckedChanged.
Respostas:
Não, você não pode fazer isso. O
onCheckedChanged
método é chamado diretamente desetChecked
. O que você pode fazer é o seguinte:Veja a fonte do CheckBox e a implementação de
setChecked
:fonte
mListener
?Checkbox
não tem um getter para o seuOnCheckChangeListener
mListener
é uma implementação daOnCheckChangedListener
interface, criada pelo programador. Minha resposta implica que o programador manteve uma referência à sua própria implementação -mListener
.CheckBox
objeto. Eu não diria que é ineficiente.Adicione esse código ao OnCheckedChangeListener:
Isso nos ajudará a descobrir que o estado do checkBox do tempo foi alterado programaticamente ou pela ação do usuário.
fonte
Outra maneira possível de conseguir isso é usar um CheckBox personalizado, que permitirá escolher se você deseja que o ouvinte seja chamado ou não:
Versão Kotlin, se você preferir:
uso da amostra:
fonte
você usa simplesmente setonclickListener, ele funciona bem e esse é um método muito simples, obrigado :)
fonte
Usando as extensões do Kotlin com a resposta @Shade:
fonte
Para quem se deparar com isso, uma maneira mais simples de fazer isso é usar uma tag na caixa de seleção e depois marcar essa tag no ouvinte (o código está no Kotlin):
Em seguida, ao acessar, basta definir a tag como true antes de definir isChecked como true quando desejar ignorar a alteração de valor:
Você também pode mapear a tag para uma chave usando o método setTag alternativo que requer uma chave se estiver preocupado com a capacidade de compreensão. Mas se tudo estiver contido em uma única classe, algumas sequências de comentários serão mais que suficientes para explicar o que está acontecendo.
fonte
Você pode usar esta classe SafeCheckBox como sua caixa de seleção:
Então você pode ligar para: -
setSafeCheck(true,ListenerMode.IGNORE);// OnCheckedChange listener will not be notified
fonte
Defina null como changeListener antes de verificar o botão de opção. Você pode definir o ouvinte novamente após verificar o botão de opção.
fonte
Minha interpretação, que eu acho que é a mais fácil,
pode ser útil)
use-o
o uso acionará a
setChecked(boolean)
funçãoque é tudo
KOTLIN
fonte
Esta é uma solução simples que eu usei:
Defina um ouvinte personalizado:
Inicialização:
Uso:
fonte
Que tal agora. Tente usar a tag no modo de exibição
e
fonte
Usei um
ReentrantLock
e bloqueio-o sempre que estou configurandoisChecked
:fonte
Basta usar um sistema de sinalizador simples com um booleano. Crie
boolean noListener
. Sempre que você deseja ativar / desativar seu interruptor sem executar nenhum código (neste exemplo, representado comorunListenerCode()
, basta definirnoListener=true
antes de chamarswitch.setChecked(false/true)
Solução muito simples usando sinalizadores simples. No final, definimos
noListener=false
mais uma vez para que nosso código continue funcionando. Espero que isto ajude!fonte
Tente este deve funcionar para você! Você pode usar isso com o firebase também!
Para obter dados do firebase! Usa isto!
Depois disso, quando o usuário faz alguma coisa!
fonte
Eu realmente não queria ter que passar pelo ouvinte a cada vez que definimos a verificação alterada, nem usando
enabled
como uma maneira de determinar se devemos definir o valor (o que acontece no caso de a chave já estar desativada ao definir o valor ?)Em vez disso, estou usando tags com um ID e alguns métodos de extensão que você pode chamar:
Agora você pode ligar
setCheckedWithoutCallingListener(bool)
e aplicará o uso correto do ouvinte.Você também pode ligar
setChecked(bool)
para disparar o ouvinte, se ainda precisar dele.fonte
Eu acho que usar a reflexão é o único caminho. Algo assim:
fonte
if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN){ /* do reflection */}
Minha solução escrita em java com base na resposta @ Chris:
2 caixas de seleção e sempre uma será marcada (uma deve ser marcada inicialmente). Configurando a tag para blocos verdadeiros no ouvinte onCheckedChanged.
fonte
fonte