121 votes

Comment puis-je distinguer si la valeur d'un commutateur ou d'une case à cocher est modifiée 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 mettre en œuvre la méthode isNotSetByUser() ?

0 votes

Je ne suis pas certain, mais je pense que si l'utilisateur l'a fait basculer, vous obtiendrez également un rappel onClick si vous définissez cet écouteur. Ainsi, vous pouvez peut-être définir un indicateur booléen dans le onClick et le vérifier dans le onCheckChanged pour voir si l'utilisateur a initié le changement.

1 votes

0 votes

J'ai une solution plus simple et plus claire : voir stackoverflow.com/a/41574200/3256989

172voto

anthropomo Points 1611

Réponse 2 :

Une réponse très simple :

Utilisation 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);
        }

    });

Désormais, vous ne captez que les événements liés aux clics et vous n'avez plus à vous soucier des changements programmatiques.


Réponse 1 :

J'ai créé une classe enveloppante (voir le modèle de décorateur) 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é de la même manière 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 voulez définir la vérification à partir du code sans déclencher l'écouteur, appelez myCheckBox.silentlySetChecked(someBoolean) au lieu de setChecked .

15 votes

Pour un Switch La réponse 1 fonctionne à la fois dans le cas d'un robinet et d'une glissière, tandis que la réponse 2 ne fonctionne que dans le cas d'un robinet. Par préférence personnelle, j'ai fait en sorte que ma classe s'étende sur les points suivants CheckBox / Switch plutôt que d'y faire référence. C'est plus propre (notez que vous devez spécifier le nom complet du paquet dans le XML si vous faites cela).

1 votes

Merci pour cet anthropomo, je ne sais pas pourquoi je n'y ai pas pensé plus tôt, mais vous m'avez fait gagner un temps précieux ;). A la vôtre !

0 votes

Je ne suis pas sûr de cela, mais si vous étendez SwitchCompat (en utilisant appcompat v7 ) pour obtenir le changement de conception matérielle, vous pouvez mettre fin à la nouvelle conception et aux capacités de teinte.

36voto

Denis Babak Points 618

Vous pouvez peut-être vérifier isShown() ? Si VRAI - alors c'est l'utilisateur. Cela fonctionne 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();
        }
    }
});

1 votes

Il fonctionne (c'est-à-dire qu'il n'y a pas de rappel inattendu) même si vous appelez "setChecked(isChecked)" dans onStart() ou onResume(). On peut donc considérer que c'est une solution parfaite.

2 votes

Il semble que ce ne soit pas une solution générale. Que se passe-t-il si le bouton est affiché à ce moment-là mais que sa valeur est modifiée à partir du code ?

1 votes

Je ne comprends pas comment 'isShown()' fait la distinction entre les actions de l'utilisateur et les changements programmatiques ? Par exemple, comment peut-on dire que c'est une action de l'utilisateur si 'isShown()' est vrai ?

4voto

Romain Piel Points 4269

Essayez d'étendre CheckBox. Quelque chose comme ça (exemple non exhaustif) :

public MyCheckBox extends CheckBox {

   private Boolean isCheckedProgramatically = false;

   public void setChecked(Boolean checked) {
       isCheckedProgramatically = true;
       super.setChecked(checked);
   }

   public Boolean isNotSetByUser() {
      return isCheckedProgramatically;
   }

}

3voto

user2036853 Points 1

Il existe une autre solution simple qui fonctionne assez bien. L'exemple est pour Switch.

public class BetterSwitch extends Switch {
  //Constructors here...

    private boolean mUserTriggered;

    // Use it in listener to check that listener is triggered 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;
    }
}

2voto

Guillaume Points 10121

Question intéressante. A ma connaissance, une fois que vous êtes dans l'écouteur, vous ne pouvez pas détecter quelle action a déclenché l'écouteur, le contexte n'est pas suffisant. Sauf si vous utilisez une valeur booléenne externe comme indicateur.

Lorsque vous cochez la case "par programme", définissez une valeur booléenne avant pour indiquer que cela a été fait par programme. 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;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X