Comment distinguer si Switch, Checkbox Value est modifié par l’utilisateur ou par programme (y compris par rétention)?

setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { // How to check whether the checkbox/switch has been checked // by user or it has been checked programatically ? if (isNotSetByUser()) return; handleSetbyUser(); } }); 

Comment implémenter la méthode isNotSetByUser() ?

Réponse 2:

Une réponse très simple:

Utiliser sur OnClickListener au lieu de OnCheckedChangeListener

  someCheckBox.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { // you might keep a reference to the CheckBox to avoid this class cast boolean checked = ((CheckBox)v).isChecked(); setSomeBoolean(checked); } }); 

Maintenant, vous ne prenez que des événements de clics et vous n’avez plus à vous soucier des changements de programme.


Réponse 1:

J’ai créé une classe wrapper (voir Decorator Pattern) qui gère ce problème de manière encapsulée:

 public class BetterCheckBox extends CheckBox { private CompoundButton.OnCheckedChangeListener myListener = null; private CheckBox myCheckBox; public BetterCheckBox(Context context) { super(context); } public BetterCheckBox(Context context, CheckBox checkBox) { this(context); this.myCheckBox = checkBox; } // assorted constructors here... @Override public void setOnCheckedChangeListener( CompoundButton.OnCheckedChangeListener listener){ if(listener != null) { this.myListener = listener; } myCheckBox.setOnCheckedChangeListener(listener); } public void silentlySetChecked(boolean checked){ toggleListener(false); myCheckBox.setChecked(checked); toggleListener(true); } private void toggleListener(boolean on){ if(on) { this.setOnCheckedChangeListener(myListener); } else { this.setOnCheckedChangeListener(null); } } } 

CheckBox peut toujours être déclaré identique en XML, mais utilisez-le lors de l’initialisation de votre interface graphique dans le code:

 BetterCheckBox myCheckBox; // later... myCheckBox = new BetterCheckBox(context, (CheckBox) view.findViewById(R.id.my_check_box)); 

Si vous souhaitez définir check from code sans déclencher le listener, appelez myCheckBox.silentlySetChecked(someBoolean) au lieu de setChecked .

Peut-être que vous pouvez vérifier isShown ()? Si TRUE – que son utilisateur. Travaille pour moi.

 setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (myCheckBox.isShown()) {// makes sure that this is shown first and user has clicked/dragged it doSometing(); } } }); 

Vous pouvez supprimer l’écouteur avant de le modifier par programmation et l’append à nouveau, comme indiqué dans le message SO suivant:

https://stackoverflow.com/a/14147300/1666070

 theCheck.setOnCheckedChangeListener(null); theCheck.setChecked(false); theCheck.setOnCheckedChangeListener(toggleButtonChangeListener); 

Inside onCheckedChanged () vérifie simplement si l’utilisateur a bien coché / désélectionné le bouton radio et effectue les opérations comme suit:

 mMySwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (buttonView.isPressed()) { // User has clicked check box } else { //sortingggered due to programmatic assignment using 'setChecked()' method. } } 

})

Essayez d’étendre CheckBox. Quelque chose comme ça (exemple pas complet):

 public MyCheckBox extends CheckBox { private Boolean isCheckedProgramatically = false; public void setChecked(Boolean checked) { isCheckedProgramatically = true; super.setChecked(checked); } public Boolean isNotSetByUser() { return isCheckedProgramatically; } } 

Il existe une autre solution simple qui fonctionne très bien. Exemple pour Switch.

 public class BetterSwitch extends Switch { //Constructors here... private boolean mUserTriggered; // Use it in listener to check that listener is sortingggered by the user. public boolean isUserTriggered() { return mUserTriggered; } // Override this method to handle the case where user drags the switch @Override public boolean onTouchEvent(MotionEvent ev) { boolean result; mUserTriggered = true; result = super.onTouchEvent(ev); mUserTriggered = false; return result; } // Override this method to handle the case where user clicks the switch @Override public boolean performClick() { boolean result; mUserTriggered = true; result = super.performClick(); mUserTriggered = false; return result; } } 

Question interessante. À ma connaissance, une fois que vous êtes dans l’auditeur, vous ne pouvez pas détecter quelle action a déclenché l’auditeur, le contexte ne suffit pas. Sauf si vous utilisez une valeur booléenne externe comme indicateur.

Lorsque vous cochez la case “par programmation”, définissez une valeur booléenne avant d’indiquer que cela a été fait par programmation. Quelque chose comme:

 private boolean boxWasCheckedProgrammatically = false; .... // Programmatic change: boxWasCheckedProgrammatically = true; checkBoxe.setChecked(true) 

Et dans votre écouteur, n’oubliez pas de réinitialiser l’état de la case à cocher:

 @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isNotSetByUser()) { resetBoxCheckSource(); return; } doSometing(); } // in your activity: public boolean isNotSetByUser() { return boxWasCheckedProgrammatically; } public void resetBoxCheckedSource() { this.boxWasCheckedProgrammatically = false; } 

Essayez NinjaSwitch :

Appelez simplement setChecked(boolean, true) pour changer l’état vérifié du commutateur sans détection!

 public class NinjaSwitch extends SwitchCompat { private OnCheckedChangeListener mCheckedChangeListener; public NinjaSwitch(Context context) { super(context); } public NinjaSwitch(Context context, AtsortingbuteSet attrs) { super(context, attrs); } public NinjaSwitch(Context context, AtsortingbuteSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public void setOnCheckedChangeListener(OnCheckedChangeListener listener) { super.setOnCheckedChangeListener(listener); mCheckedChangeListener = listener; } /** * 

Changes the checked state of this button.

* * @param checked true to check the button, false to uncheck it * @param isNinja true to change the state like a Ninja, makes no one knows about the change! */ public void setChecked(boolean checked, boolean isNinja) { if (isNinja) { super.setOnCheckedChangeListener(null); } setChecked(checked); if (isNinja) { super.setOnCheckedChangeListener(mCheckedChangeListener); } } }

Si OnClickListener est déjà défini et ne doit pas être remplacé, utilisez !buttonView.isPressed() comme isNotSetByUser() .

Sinon, la meilleure variante consiste à utiliser OnClickListener au lieu de OnCheckedChangeListener .

La réponse acceptée pourrait être légèrement simplifiée pour ne pas conserver une référence à la case à cocher d’origine. Cela fait que nous pouvons utiliser le SilentSwitchCompat (ou SilentCheckboxCompat si vous préférez) directement dans le XML. Je l’ai également fait pour que vous puissiez définir OnCheckedChangeListener sur null si vous le souhaitez.

 public class SilentSwitchCompat extends SwitchCompat { private OnCheckedChangeListener listener = null; public SilentSwitchCompat(Context context) { super(context); } public SilentSwitchCompat(Context context, AtsortingbuteSet attrs) { super(context, attrs); } @Override public void setOnCheckedChangeListener(OnCheckedChangeListener listener) { super.setOnCheckedChangeListener(listener); this.listener = listener; } /** * Check the {@link SilentSwitchCompat}, without calling the {@code onCheckChangeListener}. * * @param checked whether this {@link SilentSwitchCompat} should be checked or not. */ public void silentlySetChecked(boolean checked) { OnCheckedChangeListener tmpListener = listener; setOnCheckedChangeListener(null); setChecked(checked); setOnCheckedChangeListener(tmpListener); } } 

Vous pouvez alors l’utiliser directement dans votre XML comme ceci (Remarque: vous aurez besoin du nom complet du paquet):

  

Ensuite, vous pouvez cocher la case en appelant:

 SilentCheckBox mySilentCheckBox = (SilentCheckBox) findViewById(R.id.my_check_box) mySilentCheckBox.silentlySetChecked(someBoolean) 

Créer une variable

 boolean setByUser = false; // Initially it is set programmatically private void notSetByUser(boolean value) { setByUser = value; } // If user has changed it will be true, else false private boolean isNotSetByUser() { return setByUser; } 

Dans l’application lorsque vous le modifiez à la place de l’utilisateur, appelez notSetByUser(true) pour qu’il ne soit pas défini par l’utilisateur, sinon appelez notSetByUser(false) c’est-à-dire qu’il est défini par programme.

Enfin, dans votre écouteur d’événement, après avoir appelé isNotSetByUser (), assurez-vous de revenir à la normale.

Appelez cette méthode chaque fois que vous gérez cette action via utilisateur ou par programme. Appelez le notSetByUser () avec la valeur appropriée.

Si la balise de la vue n’est pas utilisée, vous pouvez l’utiliser au lieu d’étendre la case à cocher:

  checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) { if (buttonView.getTag() != null) { buttonView.setTag(null); return; } //handle the checking/unchecking } 

chaque fois que vous appelez quelque chose qui coche / décoche la case à cocher, appelez également ceci avant de cocher / décocher:

 checkbox.setTag(true);