web-dev-qa-db-fra.com

Comment puis-je 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()?

91
Solvek

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

    });

Désormais, vous ne récupérez que les événements liés aux clics et vous n'avez pas à vous soucier des modifications de programme.


Réponse 1:

J'ai créé une classe de wrapper (voir Motif de décorateur) qui traite 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 pour initialiser 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 une vérification à partir du code sans déclencher l'écouteur, appelez myCheckBox.silentlySetChecked(someBoolean) au lieu de setChecked.

142
anthropomo

Peut-être que vous pouvez vérifier isShown ()? Si TRUE - que c'est l'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();
        }
    }
});
36
Denis Babak

Vous pouvez supprimer l'auditeur avant de le modifier par programme et l'ajouter à nouveau, comme indiqué dans l'article SO suivant:

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

theCheck.setOnCheckedChangeListener(null);
theCheck.setChecked(false);
theCheck.setOnCheckedChangeListener(toggleButtonChangeListener);
18
Eli Kohen

Dans onCheckedChanged (), il suffit de vérifier si l’utilisateur a bien coché/décoché le bouton radio, puis procédez comme suit:

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

});

13
adi

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

public MyCheckBox extends CheckBox {

   private Boolean isCheckedProgramatically = false;

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

   public Boolean isNotSetByUser() {
      return isCheckedProgramatically;
   }

}
4
Romain Piel

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;
    }
}
3
Invised

Question interessante. À ma connaissance, une fois que vous êtes dans l'écouteur, vous ne pouvez pas détecter l'action qui l'a déclenché, 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 auditeur, 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;
}
2
Guillaume

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

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

2
Pavel

La réponse acceptée pourrait être un peu simplifiée pour ne pas conserver une référence à la case à cocher d'origine. Cela nous permet donc d'utiliser la variable SilentSwitchCompat (ou SilentCheckboxCompat si vous préférez) directement dans le fichier XML. J'ai également fait en sorte que vous puissiez définir la 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, AttributeSet 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 ensuite utiliser ceci directement dans votre XML comme suit (Remarque: vous aurez besoin du nom complet du paquet):

<com.my.package.name.SilentCheckBox
      Android:id="@+id/my_check_box"
      Android:layout_width="wrap_content"
      Android:layout_height="wrap_content"
      Android:textOff="@string/disabled"
      Android:textOn="@string/enabled"/>

Ensuite, vous pouvez cocher la case en silence en appelant:

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

Essayez NinjaSwitch:

Il suffit d'appeler setChecked(boolean, true) pour modifier l'état vérifié du commutateur sans être détecté!

public class NinjaSwitch extends SwitchCompat {

    private OnCheckedChangeListener mCheckedChangeListener;

    public NinjaSwitch(Context context) {
        super(context);
    }

    public NinjaSwitch(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public NinjaSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
        super.setOnCheckedChangeListener(listener);
        mCheckedChangeListener = listener;
    }

    /**
     * <p>Changes the checked state of this button.</p>
     *
     * @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);
        }
    }
}
2
Taeho Kim

Voici ma mise en place

Code Java pour le commutateur personnalisé:

public class CustomSwitch extends SwitchCompat {

private OnCheckedChangeListener mListener = null;

public CustomSwitch(Context context) {
    super(context);
}

public CustomSwitch(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public CustomSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

@Override
public void setOnCheckedChangeListener(@Nullable OnCheckedChangeListener listener) {
    if(listener != null && this.mListener != listener) {
        this.mListener = listener;
    }
    super.setOnCheckedChangeListener(listener);
}

public void setCheckedSilently(boolean checked){
    this.setOnCheckedChangeListener(null);
    this.setChecked(checked);
    this.setOnCheckedChangeListener(mListener);
}}

Code Kotlin équivalent:

class CustomSwitch : SwitchCompat {

private var mListener: CompoundButton.OnCheckedChangeListener? = null

constructor(context: Context) : super(context) {}

constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {}

constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}

override fun setOnCheckedChangeListener(@Nullable listener: CompoundButton.OnCheckedChangeListener?) {
    if (listener != null && this.mListener != listener) {
        this.mListener = listener
    }
    super.setOnCheckedChangeListener(listener)
}

fun setCheckedSilently(checked: Boolean) {
    this.setOnCheckedChangeListener(null)
    this.isChecked = checked
    this.setOnCheckedChangeListener(mListener)
}}

Pour changer l'état du commutateur sans déclencher l'utilisation de l'écouteur:

swSelection.setCheckedSilently(contact.isSelected)

Vous pouvez surveiller le changement d'état comme d'habitude en:

swSelection.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
   @Override
   public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
      // Do something
   }       
 });

À Kotlin:

 swSelection.setOnCheckedChangeListener{buttonView, isChecked -> run {
            contact.isSelected = isChecked
        }}
1
thilina Kj

Ma variante avec les fonctions d'extension Kotlin:

fun CheckBox.setCheckedSilently(isChecked: Boolean, onCheckedChangeListener: CompoundButton.OnCheckedChangeListener) {
    if (isChecked == this.isChecked) return
    this.setOnCheckedChangeListener(null)
    this.isChecked = isChecked
    this.setOnCheckedChangeListener(onCheckedChangeListener)
}

... malheureusement nous devons passer chaque fois onCheckedChangeListener car la classe CheckBox n’a pas de getter pour le champ mOnCheckedChangeListener

Usage:

checkbox.setCheckedSilently(true, myCheckboxListener)
1
Fedir Tsapana

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 que ce ne soit pas défini par l'utilisateur, sinon appelez notSetByUser(false) c'est-à-dire qu'il soit défini par programme.

Enfin, dans votre écouteur d'événement, après avoir appelé isNotSetByUser (), assurez-vous de le redéfinir sur normal.

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

0
Tvd

J'ai créé une extension avec PublishSubject, une version simple de RxJava. Réagit uniquement sur les événements "OnClick".

/**
 * Creates ClickListener and sends switch state on each click
 */
fun CompoundButton.onCheckChangedByUser(): PublishSubject<Boolean> {
    val onCheckChangedByUser: PublishSubject<Boolean> = PublishSubject.create()
    setOnClickListener {
        onCheckChangedByUser.onNext(isChecked)
    }
    return onCheckChangedByUser
}
0
Janusz Hain

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, appelez également ceci avant de cocher/décocher:

checkbox.setTag(true);
0
android developer