更改复选框值而不触发onCheckedChanged

时间:2013-03-20 12:10:54

标签: android checkbox

我为setOnCheckedChangeListener

实施了checkbox

有没有办法可以打电话

checkbox.setChecked(false);

不触发onCheckedChanged

20 个答案:

答案 0 :(得分:239)

不,你不能这样做。 onCheckedChanged方法直接从setChecked调用。你能做的是以下几点:

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

请参阅source of CheckBox以及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;            
    }
}

答案 1 :(得分:21)

将此代码添加到OnCheckedChangeListener中:

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

这将帮助我们找出程序或通过用户操作更改的天气复选框状态。

答案 2 :(得分:17)

实现此目的的另一种可能方法是使用自定义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);
        }
        super.toggle();
    }
}

Kotlin版本,如果您愿意:

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)
        }
        super.toggle()
    }
}

样本用法:

checkBox.setChecked(true,false);

答案 3 :(得分:12)

你只使用setonclickListener,它会正常工作,这是一个非常简单的方法,谢谢:)

答案 4 :(得分:6)

使用Kotlin的扩展@Shade回答:

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

答案 5 :(得分:4)

您可以使用此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

答案 6 :(得分:3)

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

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

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

checkBox.tag = true
checkBox.isChecked = true

如果您担心可理解性,还可以使用需要密钥的备用setTag方法将标记映射到键。但如果它全部包含在单个类中,那么一些注释字符串将足以解释发生的事情。

答案 7 :(得分:2)

在检查单选按钮之前将null设置为changeListener。您可以在检查单选按钮后再次设置监听器。

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

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

   }
});

答案 8 :(得分:2)

我认为我的解释是最容易的 可能会有帮助)

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)功能
这就是全部

<强>科特林

class MyCheckBox @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : 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)
    }
}

答案 9 :(得分:2)

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

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

答案 10 :(得分:1)

我猜使用反射是唯一的方法。像这样:

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

答案 11 :(得分:1)

我使用了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
    }
}

答案 12 :(得分:1)

  

我发现上述所有答案都太复杂了。为什么不只使用简单的布尔值创建自己的标志?

只需使用带有布尔值的简单标志系统。创建boolean noListener。每当您想在不运行任何代码的情况下打开/关闭开关(在此示例中,表示为runListenerCode()时,只需在调用noListener=true之前设置switch.setChecked(false/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,以便我们的代码继续工作。希望这会有所帮助!

答案 13 :(得分:0)

如何? 尝试在视图中使用标签

vee-validate

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

答案 14 :(得分:0)

public void setCheckedChangeListenerSwitch() {

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

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

        }
    });

}

// Update state & reset listener (so onCheckedNot called)
public void updateSwitchState(boolean state){

    switch.setOnCheckedChangeListener(null);
    switch.setChecked(state);
    setCheckedChangeListenerSwitch();

}

答案 15 :(得分:0)

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

Ctrl + Alt + Left

2个复选框,并且总是会检查一个(尽管最初必须检查一个)。将标记设置为onblock onCheckedChanged listener。

答案 16 :(得分:0)

尝试这个应该为您工作!您也可以在firebase中使用它!

获取Firebase数据!使用这个!

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

答案 17 :(得分:0)

我真的不想每次设置checked 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)来触发监听器

答案 18 :(得分:0)

非常简单,您只需在isPressed内检查setOnCheckedChangeListener

科特琳

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

答案 19 :(得分:0)

这是标记技术的一种版本,非常易于使用。

用法:

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