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

我为我的checkbox 实现了setOnCheckedChangeListener

有什么方法可以打电话

checkbox.setChecked(false);

不触发onCheckedChanged

【问题讨论】:

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

标签: android checkbox


【解决方案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;            
    }
}

【讨论】:

  • 你建议如何获得mListener? Checkbox 的 OnCheckChangeListener 没有吸气剂
  • 好吧,没有必要仅仅因为您不了解解决方案而投反对票。 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() 在语音助手模式下双击触发时不为真。
  • 解决数据绑定问题的唯一解决方案(另请参阅stackoverflow.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
                            

                            【讨论】:

                              猜你喜欢
                              • 1970-01-01
                              • 2014-09-05
                              • 2011-01-07
                              • 2023-01-29
                              • 2018-02-11
                              • 2013-08-03
                              • 2013-07-08
                              • 2010-10-30
                              • 2014-09-24
                              相关资源
                              最近更新 更多