如何区分开关,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()

答案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:

我创build了一个包装类(见装饰模式),它以封装的方式处理这个问题:

 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

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

您可以在以编程方式更改侦听器之前删除侦听器,然后再次添加,如以下SOpost所回答的:

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

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

尝试扩展CheckBox。 类似的东西(不完整的例子):

 public MyCheckBox extends CheckBox { private Boolean isCheckedProgramatically = false; public void setChecked(Boolean checked) { isCheckedProgramatically = true; super.setChecked(checked); } public Boolean isNotSetByUser() { return isCheckedProgramatically; } } 

还有另一个简单的解决scheme,工作得很好。 例子是开关。

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

有趣的问题。 据我所知,一旦你在听众中,你无法察觉到什么动作触发了听众,上下文是不够的。 除非你使用外部布尔值作为指标。

当您select“编程”框时,先设置一个布尔值来表示它是以编程方式完成的。 就像是:

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

而在听众中,不要忘记重置checkbox的状态:

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

接受的答案可以简化一点,不保留对原始checkbox的引用。 这使得我们可以直接在XML中使用SilentSwitchCompat (如果您愿意,也可以使用SilentSwitchCompat )。 我也做了这个,所以你可以设置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) 

创build一个variables

 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:

  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的东西,在检查/取消选中之前也调用它:

 checkbox.setTag(true); 

如果OnClickListener已经设置,不应该被覆盖,请使用!buttonView.isPressed()作为isNotSetByUser()

否则,最好的变体是使用OnClickListener而不是OnCheckedChangeListener

在onCheckedChanged()里面,检查用户是否已经检查/取消选中了单选button,然后按照如下方式做相应的操作:

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

});