Как я могу различить, изменяется ли Switch, Checkbox Value пользователем или программно (в том числе путем сохранения)? - PullRequest
96 голосов
/ 03 февраля 2012
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();
            }
        });

Как реализовать метод isNotSetByUser()?

Ответы [ 15 ]

146 голосов
/ 14 января 2013

Ответ 2:

Очень простой ответ:

Использовать на OnClickListener вместо 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);
        }

    });

Теперь вы выбираете только события щелчка и вам не нужно беспокоиться о программных изменениях.


Ответ 1:

Я создал класс-оболочку (см. Шаблон декоратора), который обрабатывает эту проблему инкапсулированным способом:

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 все еще может быть объявлен тем же в XML, но используйте это при инициализации вашего GUI в коде:

BetterCheckBox myCheckBox;

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

Если вы хотите установить флажок из кода без запуска прослушивателя, позвоните myCheckBox.silentlySetChecked(someBoolean) вместо setChecked.

36 голосов
/ 03 февраля 2012

Может быть, вы можете проверить isShown ()? Если ИСТИНА - то это пользователь. У меня работает.

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();
        }
    }
});
19 голосов
/ 06 июля 2015

Вы можете удалить слушателя перед программным изменением его и добавить его снова, как ответили в следующем сообщении SO:

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

theCheck.setOnCheckedChangeListener(null);
theCheck.setChecked(false);
theCheck.setOnCheckedChangeListener(toggleButtonChangeListener);
15 голосов
/ 06 октября 2017

Внутри onCheckedChanged () просто проверьте, действительно ли пользователь установил / снял отметку с переключателя, а затем выполните соответствующие действия следующим образом:

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.   
         }
}

});

4 голосов
/ 03 февраля 2012

Попробуйте расширить CheckBox.Примерно так (не полный пример):

public MyCheckBox extends CheckBox {

   private Boolean isCheckedProgramatically = false;

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

   public Boolean isNotSetByUser() {
      return isCheckedProgramatically;
   }

}
3 голосов
/ 26 августа 2014

Есть еще одно простое решение, которое работает довольно хорошо. Пример для 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;
    }
}
2 голосов
/ 06 июля 2017

Принятый ответ может быть немного упрощен, чтобы не поддерживать ссылку на исходный флажок. Это позволяет нам использовать SilentSwitchCompat (или SilentCheckboxCompat, если хотите) непосредственно в XML. Я также сделал это, чтобы вы могли установить OnCheckedChangeListener на null, если вы хотите это сделать.

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

Затем вы можете использовать это непосредственно в своем XML-файле следующим образом (Примечание: вам понадобится полное имя пакета):

<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"/>

Затем вы можете установить флажок молча, позвонив по номеру:

SilentCheckBox mySilentCheckBox = (SilentCheckBox) findViewById(R.id.my_check_box)
mySilentCheckBox.silentlySetChecked(someBoolean)
2 голосов
/ 25 июня 2017

Если OnClickListener уже установлен и не должен быть перезаписан, используйте !buttonView.isPressed() как isNotSetByUser().

В противном случае лучший вариант - использовать OnClickListener вместо OnCheckedChangeListener.

2 голосов
/ 14 января 2016

Попробуйте NinjaSwitch:

Просто позвоните setChecked(boolean, true), чтобы изменить проверенное состояние коммутатора без обнаружения!

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 голосов
/ 03 февраля 2012

Интересный вопрос.Насколько мне известно, когда вы находитесь в слушателе, вы не можете определить, какое действие вызвало слушателя, недостаточно контекста.Если вы не используете внешнее логическое значение в качестве индикатора.

Если вы установите флажок «программно», установите логическое значение, чтобы указать, что это было сделано программно.Что-то вроде:

private boolean boxWasCheckedProgrammatically = false;

....

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

И в своем слушателе не забудьте сбросить состояние флажка:

@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;
}
...