不触发onCheckChanged的情况下更改复选框的值

190

我已经为我的复选框实现了setOnCheckedChangeListener

有没有办法可以调用?

checkbox.setChecked(false);

在不触发onCheckedChanged的情况下


为什么不使用一个简单的真/假标志呢?这是解决这个问题最简单的方法,只需要额外编写三行代码即可。请参见下面的答案。 - Ruchir Baronia
我认为最好的解决方案在这里给出: https://dev59.com/UGox5IYBdhLWcg3wjE9i#14307643 - Salman khan
21个回答

318

不,你不能这样做。 onCheckedChanged 方法直接从 setChecked 调用。 但你可以做以下操作:

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

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

7
您打算如何获取 mListenerCheckbox 没有其 OnCheckChangeListener 的 getter 方法。 - tir38
28
没必要因为你不明白解决方案就给它点踩。mListenerOnCheckChangedListener 接口的一个实现,是由程序员创建的。我的回答意味着程序员保持了对他们自己实现的引用 - mListener。请注意不改变原来的意思。 - Shade
如果您想要重复使用setChecked()方法,更改监听器是否会导致效率低下? - Ren
4
@Ren,“更改侦听器”仅涉及在“CheckBox”对象中设置一个属性。我不会说这是低效的。 - Shade
@TheRealChx101 嗯,“注入自己的处理程序”和“最好的方法是子类化系统组件”听起来不像是依赖注入 :) 无论如何,这个答案对于原生Android是有效的,我很高兴你找到了一种让它适用于你的方法。 - Shade
显示剩余5条评论

123
将此代码添加到OnCheckedChangeListener中:
if(!compoundButton.isPressed()) {
            return;
}

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


17
注意!这会破坏可访问性模式!当由语音助手模式的双击触发时,isPressed()不为true。 - A1m
解决 DataBinding 问题的唯一方案(参见 https://dev59.com/81gQ5IYBdhLWcg3wfUHp)。 - CoolMind

31

另一种可能的实现方式是使用自定义 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();
    }
}

如果您更喜欢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)
            return
        }
        super.toggle()
    }
}

使用示例:

checkBox.setChecked(true,false);

现在也可以在我的代码库中找到:

https://github.com/AndroidDeveloperLB/CommonUtils


14

对于任何偶然发现这篇文章的人,一个更简单的方法是在复选框上使用标签,然后在其侦听器中检查该标签(代码使用 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相比。谢谢。 - alierdogan7
简单的解决方案,非常适合我的需求,谢谢。 - Murli
这是一个不错的解决方案,但如果您需要在多个地方使用此逻辑,则自定义类可能更好。 - htafoya

11

如果您使用setonclickListener,它将能够正常工作,这是一种非常简单的方法,谢谢 :)


39
当用户滑动开关时,onClick() 方法不会被调用。 - James
3
确实,这么做会导致当用户拖动开关时无法处理。 - Victor G
那么你如何获取isChecked的值,即true或false? - Abhi

11
非常简单,您只需在 setOnCheckedChangeListener 中检查 isPressed
Kotlin
switch.setOnCheckedChangeListener { buttonView, isChecked ->
    when {        
        buttonView.isPressed -> {
            foo(isChecked)
        }
    }

@NicolasDuponchel,在上面的答案中,krisDrOid提出了这个解决方案,但它有一个缺点。但对于DataBinding来说,这是一个不错的选择。 - CoolMind

8

使用Kotlin扩展与@Shade的答案:

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

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监听器将不会被通知


3

我认为以下是最简单易懂的解释(希望能对您有所帮助)

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

3

你可以尝试在视图中使用标签,这样做如何?

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
            }

        }
    });

工作完美,谢谢你的解决方案。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接