更改复选框值而不触发 onCheckChanged

我已经为我的 checkbox实现了 setOnCheckedChangeListener

我能不能打个电话

checkbox.setChecked(false);

而不触发 onCheckedChanged

77088 次浏览

不,你不能这么做。 onCheckedChanged方法是从 setChecked直接调用的。你能做的是:

mCheck.setOnCheckedChangeListener (null);
mCheck.setChecked (false);
mCheck.setOnCheckedChangeListener (mListener);

参见 复选框的来源setChecked的实现:

public void  setChecked(boolean checked) {
if (mChecked != checked) {
mChecked = checked;
refreshDrawableState();


// Avoid infinite recursions if setChecked() is called from a listener
if (mBroadcasting) {
return;
}


mBroadcasting = true;
if (mOnCheckedChangeListener != null) {
mOnCheckedChangeListener.onCheckedChanged(this, mChecked);
}


if (mOnCheckedChangeWidgetListener != null) {
mOnCheckedChangeWidgetListener.onCheckedChanged(this, mChecked);
}


mBroadcasting = false;
}
}

我想使用反射是唯一的方法,就像这样:

CheckBox cb = (CheckBox) findViewById(R.id.checkBox1);
try {
Field field = CompoundButton.class.getDeclaredField("mChecked");
field.setAccessible(true);
field.set(cb, cb.isChecked());
cb.refreshDrawableState();
cb.invalidate();
} catch (Exception e) {
e.printStackTrace();
}

只要使用 setonclickListener,就可以很好地工作,这是一个非常简单的方法,谢谢:)

另一种可能的实现方法是使用自定义的 CheckBox,它允许您选择是否要调用侦听器:

public class CheckBox extends AppCompatCheckBox {
private OnCheckedChangeListener mListener;


public CheckBox(final Context context) {
super(context);
}


public CheckBox(final Context context, final AttributeSet attrs) {
super(context, attrs);
}


public CheckBox(final Context context, final AttributeSet attrs, final int defStyleAttr) {
super(context, attrs, defStyleAttr);
}


@Override
public void setOnCheckedChangeListener(final OnCheckedChangeListener listener) {
mListener = listener;
super.setOnCheckedChangeListener(listener);
}


public void setChecked(final boolean checked, final boolean alsoNotify) {
if (!alsoNotify) {
super.setOnCheckedChangeListener(null);
super.setChecked(checked);
super.setOnCheckedChangeListener(mListener);
return;
}
super.setChecked(checked);
}


public void toggle(boolean alsoNotify) {
if (!alsoNotify) {
super.setOnCheckedChangeListener(null);
super.toggle();
super.setOnCheckedChangeListener(mListener);
return;
}
super.toggle();
}
}

科特林版本,如果你喜欢的话:

class CheckBox @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : AppCompatCheckBox(context, attrs, defStyleAttr) {
private var listener: CompoundButton.OnCheckedChangeListener? = null


override fun setOnCheckedChangeListener(listener: CompoundButton.OnCheckedChangeListener?) {
this.listener = listener
super.setOnCheckedChangeListener(listener)
}


fun setChecked(checked: Boolean, alsoNotify: Boolean) {
if (!alsoNotify) {
super.setOnCheckedChangeListener(null)
super.setChecked(checked)
super.setOnCheckedChangeListener(listener)
return
}
super.setChecked(checked)
}


fun toggle(alsoNotify: Boolean) {
if (!alsoNotify) {
super.setOnCheckedChangeListener(null)
super.toggle()
super.setOnCheckedChangeListener(listener)
return
}
super.toggle()
}
}

使用方法:

checkBox.setChecked(true,false);

现在也可以在我的存储库中找到:

Https://github.com/androiddeveloperlb/commonutils

您可以使用这个 SafeCheckBox 类作为复选框:

public class SafeCheckBox extends AppCompatCheckBox implements CompoundButton.OnCheckedChangeListener {


private OnSafeCheckedListener onSafeCheckedListener;


private int mIgnoreListener = CALL_LISTENER;


public static final int IGNORE = 0;
public static final int CALL_LISTENER = 1;


@Retention(RetentionPolicy.SOURCE)
@IntDef({IGNORE, CALL_LISTENER})
public @interface ListenerMode {
}


public SafeCheckBox(Context context) {
super(context);
init(context);
}


public SafeCheckBox(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}


public SafeCheckBox(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}


/**
* @param checkState     change state of the checkbox to
* @param mIgnoreListener true to ignore the listener else listener will be  notified
*/
public void setSafeCheck(boolean checkState, @ListenerMode int mIgnoreListener) {
if (isChecked() == checkState) return; //already in the same state no need to fire listener.


if (onSafeCheckedListener != null) { // this to avoid a bug if the user listens for the event after using this method and in that case he will miss first check
this.mIgnoreListener = mIgnoreListener;
} else {
this.mIgnoreListener = CALL_LISTENER;
}
setChecked(checkState);
}


private void init(Context context) {
setOnCheckedChangeListener(this);
}




public OnSafeCheckedListener getOnSafeCheckedListener() {
return onSafeCheckedListener;
}


public void setOnSafeCheckedListener(OnSafeCheckedListener onSafeCheckedListener) {
this.onSafeCheckedListener = onSafeCheckedListener;
}


@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {


if (onSafeCheckedListener != null)
onSafeCheckedListener.onAlwaysCalledListener(buttonView, isChecked);// this has to be called before onCheckedChange
if (onSafeCheckedListener != null && (mIgnoreListener == CALL_LISTENER)) {
onSafeCheckedListener.onCheckedChanged(buttonView, isChecked);
}
mIgnoreListener = CALL_LISTENER;
}


/**
* Listener that will be called when you want it to be called.
* On checked change listeners are called even when the setElementChecked is called from code. :(
*/
public interface OnSafeCheckedListener extends OnCheckedChangeListener {
void onAlwaysCalledListener(CompoundButton buttonView, boolean isChecked);
}
}
  • 然后你可以打电话:-

    setSafeCheck(true,ListenerMode.IGNORE);// OnCheckedChange listener will not be notified

使用 Kotlin 的扩展名@Shade 回答:

fun CompoundButton.setCustomChecked(value: Boolean,listener: CompoundButton.OnCheckedChangeListener) {
setOnCheckedChangeListener(null)
isChecked = value
setOnCheckedChangeListener(listener)
}

对于任何偶然发现这个问题的人来说,一个更简单的方法就是在复选框中使用一个标记,然后在它的监听器上检查这个标记(代码在 Kotlin) :

checkBox.tag = false
checkBox.setOnCheckedChangeListener{ buttonView, isChecked ->
if(checkBox.tag != true) {
// Do some stuff
} else {
checkBox.tag = false
}

然后,在访问时,只需在想要忽略值更改时,将标记设置为 true,然后再将 isChecked 设置为 true:

checkBox.tag = true
checkBox.isChecked = true

您还可以使用替代的 setTag 方法将标记映射到一个键,如果您担心可理解性,则需要一个键。但是如果它全部包含在一个类中,那么几个注释字符串就足以解释发生了什么。

在选中单选按钮之前将 null 设置为 changeListener。可以在选中单选按钮之后再次设置侦听器。

radioGroup.setOnCheckedChangeListener(null);
radioGroup.check(R.id.radioButton);
radioGroup.setOnCheckedChangeListener(new


RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup radioGroup, @IdRes int i) {


}
});

我的解决方案是基于@Chris 的回答:

chkParent.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(buttonView.getTag() != null){
buttonView.setTag(null);
return;
}
if(isChecked){
chkChild.setTag(true);
chkChild.setChecked(false);
}
else{
chkParent.setChecked(true);
}
}
});


chkChild.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(buttonView.getTag() != null){
buttonView.setTag(null);
return;
}
if(isChecked){
chkParent.setTag(true);
chkParent.setChecked(false);
}
else{
chkChild.setChecked(true);
}
}
});

2个复选框,并且总是选中一个(但是一开始必须选中一个)。将标记设置为 true 块 onCheckedChanged 侦听器。

我的解释,我认为是最简单的
可能会有帮助)

public class ProgrammableSwitchCompat extends SwitchCompat {


public boolean isCheckedProgrammatically = false;


public ProgrammableSwitchCompat(final Context context) {
super(context);
}


public ProgrammableSwitchCompat(final Context context, final AttributeSet attrs) {
super(context, attrs);
}


public ProgrammableSwitchCompat(final Context context, final AttributeSet attrs, final int defStyleAttr) {
super(context, attrs, defStyleAttr);
}


@Override
public void setChecked(boolean checked) {
isCheckedProgrammatically = false;
super.setChecked(checked);
}


public void setCheckedProgrammatically(boolean checked) {
isCheckedProgrammatically = true;
super.setChecked(checked);
}
}

好好利用

@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean on) {
if (((ProgrammableSwitchCompat) compoundButton).isCheckedProgrammatically) {
return;
}
//...
((ProgrammableSwitchCompat) compoundButton).setCheckedProgrammatically(true);
//...
((ProgrammableSwitchCompat) compoundButton).setCheckedProgrammatically(false);
//...
}

使用将触发 setChecked(boolean)函数
仅此而已

KOTLIN

class MyCheckBox @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = R.attr.switchStyle
) : AppCompatCheckBox(context, attrs, defStyleAttr) {


var programmatically = false


override fun setChecked(checked: Boolean) {
programmatically = false
super.setChecked(checked)
}


fun setCheckedProgrammatically(checked: Boolean) {
programmatically = true
super.setChecked(checked)
}
}

这是我使用的一个简单的解决方案:
定义自定义侦听器:

class CompoundButtonListener implements CompoundButton.OnCheckedChangeListener {


boolean enabled = false;


@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {


}


void enable() {
enabled = true;
}


void disable() {
enabled = false;
}


boolean isEnabled() {
return enabled;
}
}

初始化:

CompoundButtonListener checkBoxListener = new CompoundButtonListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
if (isEnabled()) {
// Your code goes here
}
}
};
myCheckBox.setOnCheckedChangeListener(checkBoxListener);

用法:

checkBoxListener.disable();


// Some logic based on which you will modify CheckBox state
// Example: myCheckBox.setChecked(true)


checkBoxListener.enable();

这样吧。 尝试在视图中使用标记

mCheck.setTag("ignore");
mCheck.setChecked(true);
mCheck.setTag(null);

还有

switch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean selected) {


//If switch has a tag, ignore below
if(compoundButton.getTag() != null)
return;


if (selected) {
// do something
} else {
// do something else
}


}
});

我发现以上所有的答案都太复杂了。为什么不用一个简单的布尔值来创建你自己的标志呢?

只需使用一个简单的带有布尔值的标志系统。创建 boolean noListener。无论何时,只要您想在不运行任何代码的情况下打开/关闭开关(在本例中,表示为 runListenerCode(),只需在调用 switch.setChecked(false/true)之前设置 noListener=true即可)

switch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean selected) {
if (!noListener) { //If we want to run our code like usual
runListenerCode();
} else { //If we simply want the switch to turn off
noListener = false;
}
});

使用简单标志的非常简单的解决方案。最后,我们再次设置 noListener=false,以便我们的代码继续工作。希望这个能帮上忙!

在 OnCheckedChangeListener 中添加以下代码:

if(!compoundButton.isPressed()) {
return;
}

这将帮助我们确定 checkBox 状态是通过编程还是通过用户操作改变的。

我使用了一个 ReentrantLock,并在设置 isChecked时锁定它:

科特林:

// lock when isChecked is being set programmatically
val isBeingProgrammaticallySet = ReentrantLock()


// set isChecked programmatically
isBeingProgrammaticallySet.withLock()
{
checkbox.isChecked = true
}


// do something only when preference is modified by user
checkbox.setOnCheckedChangeListener()
{
_,isChecked ->
if (isBeingProgrammaticallySet.isHeldByCurrentThread.not())
{
// do it
}
}

尝试这一个应该为您工作! 您可以使用这与火基地也!

用来获取火力数据! 用这个!

databaseReference.child(user.getPhoneNumber()).child("Reqs").addValueEventListener(new ValueEventListener() {


@Override
public void onDataChange(DataSnapshot dataSnapshot) {
SharedPreferences prefs = mContext.getSharedPreferences("uinfo", MODE_PRIVATE);
String pno = prefs.getString("username", "No name defined");


if(dataSnapshot.child(pno).getValue(String.class).equals("acc")){
holder.acc.setChecked(true);
}else{
holder.acc.setChecked(false);
}
}


@Override
public void onCancelled(DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w("dfs", "loadPost:onCancelled", databaseError.toException());
// ...
}
});

之后当用户做一些事情!

holder.acc.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked) {
if(buttonView.isPressed()) {
//your code
}
}
else {
if(buttonView.isPressed()) {
//your code
}
}
}
});

我真的不想在每次设置 check change 时都传递侦听器,也不想使用 enabled作为确定是否应该设置值的方法(如果在设置值时已经禁用了开关,会发生什么情况?)

相反,我使用了带有 id 的标记和几个可以调用的扩展方法:

fun CompoundButton.setOnCheckedWithoutCallingChangeListener(
listener: (view: CompoundButton, checked: Boolean) -> Unit
) {
setOnCheckedChangeListener { view, checked ->
if (view.getTag(R.id.compound_button_checked_changed_listener_disabled) != true) {
listener(view, checked)
}
}
this.setTag(R.id.compound_button_enabled_checked_change_supported, true)
}


fun CompoundButton.setCheckedWithoutCallingListener(checked: Boolean) {
check(this.getTag(R.id.compound_button_enabled_checked_change_supported) == true) {
"Must set listener using `setOnCheckedWithoutCallingChangeListener` to call this method"
}


setTag(R.id.compound_button_checked_changed_listener_disabled, true)
isChecked = checked
setTag(R.id.compound_button_checked_changed_listener_disabled, false)
}

现在您可以调用 setCheckedWithoutCallingListener(bool),它将强制执行正确的侦听器用法。

如果仍然需要侦听器,还可以调用 setChecked(bool)来激活它

很简单,你只需要在 setOnCheckedChangeListener中检查 isPressed

科特林

switch.setOnCheckedChangeListener { buttonView, isChecked ->
when {
buttonView.isPressed -> {
foo(isChecked)
}
}

下面是一个非常容易使用的标记技术版本。

用法:

// Pass true to enable bypassing of the listener
button.setOnCheckedChangedListener(true) { _, isChecked ->
// your usual code
}


// Use extension function to set the value and bypass the listener
button.setCheckedSilently(true)

它通过几个实用扩展函数完成:

inline fun CompoundButton.setOnCheckedChangeListener(canBypass: Boolean, crossinline listener: (CompoundButton, Boolean) -> Unit) {
if (canBypass) {
setOnCheckedChangeListener { view, isChecked ->
if (view.tag != ListenerBypass) {
listener(view, isChecked)
}
}
} else {
setOnCheckedChangeListener { view, isChecked -> listener(view, isChecked) }
}
}


fun CompoundButton.setCheckedSilently(isChecked: Boolean) {
val previousTag = tag
tag = ListenerBypass
this.isChecked = isChecked
tag = previousTag
}


object ListenerBypass

onCreate()检查过一次 isPressed()。 在 onCheckChangeListener之后添加 checkbox.isPressed(),以便在每次复选框更改时检查。