更改复选框值而不触发 onCheckChanged

Posted

技术标签:

【中文标题】更改复选框值而不触发 onCheckChanged【英文标题】:Change Checkbox value without triggering onCheckChanged 【发布时间】:2013-03-09 12:35:09 【问题描述】:

我为我的checkbox 实现了setOnCheckedChangeListener

有什么方法可以打电话

checkbox.setChecked(false);

不触发onCheckedChanged

【问题讨论】:

为什么不只使用一个简单的真/假标志呢?这是解决这个问题的最简单的方法,只需要三行额外的代码。请参阅下面的答案。 我认为这里给出了最好的解决方案***.com/questions/9129858/… 【参考方案1】:

不,你不能这样做。 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;            
    

【讨论】:

你建议如何获得mListenerCheckboxOnCheckChangeListener 没有吸气剂 好吧,没有必要仅仅因为您不了解解决方案而投反对票。 mListener 是由程序员创建的OnCheckChangedListener 接口的实现。我的回答暗示程序员维护了对他们自己的实现的引用 - mListener. 如果要重复使用setChecked()方法,换个监听会不会效率低? @Ren,更改侦听器仅涉及设置CheckBox 对象中的属性。我不会说那是低效的。 The doc reads "Called when the checked radio button has changed. When the selection is cleared, checkedId is -1".这确实具有误导性,它也应该通过 isChecked 。【参考方案2】:

在 OnCheckedChangeListener 中添加这段代码:

if(!compoundButton.isPressed()) 
            return;

这将帮助我们确定天气复选框的状态是否以编程方式或通过用户操作进行了更改。

【讨论】:

注意!这打破了可访问性模式! isPressed() 在语音助手模式下双击触发时不为真。 解决数据绑定问题的唯一解决方案(另请参阅***.com/questions/42666559/…)。【参考方案3】:

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

【讨论】:

【参考方案4】:

您只需使用 setonclickListener ,它就可以正常工作,这是非常简单的方法,谢谢 :)

【讨论】:

用户滑动开关时不会调用onClick()。 确实,当用户拖动切换开关时,这样做会让您没有处理程序。 那么你将如何获得 isChecked 值,即 true 或 false?【参考方案5】:

对于任何偶然发现这一点的人,一种更简单的方法是在复选框上使用一个标签,然后在其侦听器上检查该标签(代码在 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 相比,这是一个更简洁的解决方案。谢谢。【参考方案6】:

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

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

【讨论】:

【参考方案7】:

很简单,你只需检查isPressed里面的setOnCheckedChangeListener

科特林

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

【讨论】:

@NicolasDuponchel,在上面的答案中 krisDrOid 提出了这个解决方案,它有一个缺点。但对于 DataBinding 它是一个很好的变体。【参考方案8】:

你可以使用这个 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

【讨论】:

【参考方案9】:

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

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

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

   
);

【讨论】:

【参考方案10】:

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

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

【讨论】:

【参考方案11】:

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

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

【讨论】:

【参考方案12】:

这个怎么样。 尝试在视图中使用标签

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
            

        
    );

【讨论】:

【参考方案13】:

试试这个应该适合你!您也可以将其与 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
                    
                
            
        );

【讨论】:

【参考方案14】:

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

只需使用带有布尔值的简单标志系统。创建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,以便我们的代码继续工作。希望这会有所帮助!

【讨论】:

【参考方案15】:

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

【讨论】:

请不要在 Java 问题上发布 Kotlin 代码。谢谢 @CaptainCrunch,多么奇怪的评论。现在我们几乎都在使用 Kotlin,而且这个解决方案没有 Kotlin 特定的代码。【参考方案16】:

isPressed()onCreate() 中检查过一次。 在您的 onCheckChangeListener 之后添加 checkbox.isPressed() 以在您的复选框每次更改时进行检查。

【讨论】:

【参考方案17】:

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

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

【讨论】:

在开发人员更改字段名称之前可能有效,或者,例如,在层次结构中拉起“isChecked”方法......或进行另一次重构......至少添加类似if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) /* do reflection */ 鼓励破坏 API 并深入研究内部是一个错误的想法。对实现的任何更改都会导致应用失败。【参考方案18】:

我的解决方案是基于@Chris 的答案用 java 编写的:

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 侦听器。

【讨论】:

【参考方案19】:
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();


【讨论】:

【参考方案20】:

我真的不想每次我们设置检查更改时都必须传递侦听器,也不想使用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) 来触发监听器

【讨论】:

【参考方案21】:

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

用法:

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

【讨论】:

以上是关于更改复选框值而不触发 onCheckChanged的主要内容,如果未能解决你的问题,请参考以下文章

更新角度范围而不触发更改事件

在 Sencha Touch 中设置 numberfield 的值而不触发“change”事件

将表单的所有输入重置为默认值而不重新加载

使用类中的值设置等于新值而不更改先前值c ++

在 SwiftUI 中捕获文本字段值而不按返回键 [重复]

编辑文本控制器更新小部件值而不调用 setState() - Flutter