Как я могу отличить, изменяется ли значение Switch, Checkbox пользователем или программно (в том числе по удержанию)?
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()
?
13 ответов:
ответ 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); } } }
флажок все еще может быть объявлен таким же в XML, но используйте это при инициализации графического интерфейса в коде:
BetterCheckBox myCheckBox; // later... myCheckBox = new BetterCheckBox(context, (CheckBox) view.findViewById(R.id.my_check_box));
если вы хотите установить флажок из кода без запуска слушателя, вызовите
myCheckBox.silentlySetChecked(someBoolean)
вместоsetChecked
.
может быть, вы можете проверить isShown()? Если TRUE-то это пользователь. Работать на меня.
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(); } } });
вы можете удалить прослушиватель перед его программным изменением и добавить его снова, как указано в следующем сообщении SO:
https://stackoverflow.com/a/14147300/1666070
theCheck.setOnCheckedChangeListener(null); theCheck.setChecked(false); theCheck.setOnCheckedChangeListener(toggleButtonChangeListener);
внутри 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. } }
});
попробуйте расширить флажок. Что-то вроде этого (не полный пример):
public MyCheckBox extends CheckBox { private Boolean isCheckedProgramatically = false; public void setChecked(Boolean checked) { isCheckedProgramatically = true; super.setChecked(checked); } public Boolean isNotSetByUser() { return isCheckedProgramatically; } }
есть еще одно простое решение, которое работает довольно хорошо. Пример для коммутатора.
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; } }
интересный вопрос. Насколько мне известно, когда вы находитесь в слушателе, вы не можете определить, какое действие вызвало слушателя, контекста недостаточно. Если вы не используете внешнее логическое значение в качестве индикатора.
когда вы устанавливаете флажок "программно", установите логическое значение, прежде чем указать, что это было сделано программно. Что-то вроде:
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; }
попробовать
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); } } }
если
OnClickListener
уже установлен и не должен быть перезаписан, используйте!buttonView.isPressed()
какisNotSetByUser()
.в противном случае лучший вариант-использовать
OnClickListener
вместоOnCheckedChangeListener
.
принятый ответ может быть немного упрощен, чтобы не поддерживать ссылку на исходный флажок. Это значит, что мы можем использовать
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)
создать переменную
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; }
в приложении, когда вы меняете его вместо пользователя, вызовите
notSetByUser(true)
Так что он не установлен пользователем, иначе вызовитеnotSetByUser(false)
т. е. он устанавливается программой.наконец, в вашем прослушивателе событий, после вызова isNotSetByUser (), убедитесь, что вы снова измените его на нормальный.
вызовите этот метод всякий раз, когда вы обрабатываете это действие либо через пользователя, либо программно. Вызовите notSetByUser () с соответствующим значением.
если тег представления не используется, Вы можете использовать его вместо расширения флажка:
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 }
каждый раз, когда вы вызываете что-то, что проверяет/снимает флажок, также вызывайте это перед проверкой/снятием флажка :
checkbox.setTag(true);
вот моя реализация
Java-код для пользовательского переключателя :
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); }}
Эквивалентный Код Котлина:
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) }}
чтобы изменить состояние переключателя без запуска прослушивателя используйте:
swSelection.setCheckedSilently(contact.isSelected)
вы можете контролировать изменение состояния, как обычно:
swSelection.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { // Do something } });
В Котлин :
swSelection.setOnCheckedChangeListener{buttonView, isChecked -> run { contact.isSelected = isChecked }}