How can I distinguish whether Switch,Checkbox Value is changed by user or programmatically (including by retention)?

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

How to implement method isNotSetByUser()?

35073 次浏览

也许你可以检查 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();
}
}
});

有趣的问题。据我所知,一旦你在听众中,你不能发现什么行动触发了听众,上下文是不够的。除非使用外部布尔值作为指示器。

当你选中“以编程方式”时,先设置一个布尔值来表示它是以编程方式完成的,比如:

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

Try extending CheckBox. Something like that (not complete example):

public MyCheckBox extends CheckBox {


private Boolean isCheckedProgramatically = false;


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


public Boolean isNotSetByUser() {
return isCheckedProgramatically;
}


}

创建一个变量

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 ()之后,确保再次将其更改为正常。

无论您是通过用户还是通过编程方式处理该操作,都可以调用此方法。

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


});

现在您只接收单击事件,不必担心程序更改。


Answer 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

如果没有使用视图的标记,您可以使用它来代替扩展复选框:

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

There is another simple solution that works pretty well. Example is for 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;
}
}

You can remove the listener before changing it programatically and add it again, as answered in the following SO post:

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

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

试试 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

接受的答案可以简化一点,以便不维护对原始复选框的引用。这使得我们可以直接在 XML 中使用 SilentSwitchCompat(或者 SilentCheckboxCompat,如果您喜欢的话)。我也使它这样你可以设置的 OnCheckedChangeListenernull,如果你愿意这样做。

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)

Inside the onCheckedChanged() just check whether the user has actually checked/unchecked the radio button and then do the stuff accordingly as follows:

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

这是我的实现

用于自定义交换机的 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
}
});

In Kotlin :

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

我的 Kotlin 扩展函数变体:

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

... 不幸的是,我们每次都需要传递 onCheckedChangeListener,因为 CheckBox 类没有 getter for mOnCheckedChangeListener 字段((

用法:

checkbox.setCheckedSilently(true, myCheckboxListener)

我用 RxJava 的 PublishSubject创建了一个简单的扩展,只对“ 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
}

这应该足够了:

SwitchCompact.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (buttonView.isPressed()) {
if (!isChecked) {
//do something
} else {
// do something else
}
}
});