如何区分开关,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
)。 我也做了这个,所以你可以设置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)
创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. } }
});