如何区分Switch,Checkbox Value是由用户还是以编程方式更改(包括保留)?

时间:2012-02-03 14:05:58

标签: android checkbox listener onchange

setOnCheckedChangeListener(new OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                // How to check whether the checkbox/switch has been checked
                // by user or it has been checked programatically ?

                if (isNotSetByUser())
                    return;
                handleSetbyUser();
            }
        });

如何实施方法isNotSetByUser()

15 个答案:

答案 0 :(得分:145)

答案2:

一个非常简单的答案:

使用OnClickListener而不是OnCheckedChangeListener

    someCheckBox.setOnClickListener(new OnClickListener(){

        @Override
        public void onClick(View v) {
            // you might keep a reference to the CheckBox to avoid this class cast
            boolean checked = ((CheckBox)v).isChecked();
            setSomeBoolean(checked);
        }

    });

现在,您只需选择点击事件,而无需担心程序化更改。


答案1:

我创建了一个包装类(参见Decorator Pattern),它以封装的方式处理这个问题:

public class BetterCheckBox extends CheckBox {
    private CompoundButton.OnCheckedChangeListener myListener = null;
    private CheckBox myCheckBox;

    public BetterCheckBox(Context context) {
        super(context);
    }

    public BetterCheckBox(Context context, CheckBox checkBox) {
        this(context);
        this.myCheckBox = checkBox;
    }

    // assorted constructors here...    

    @Override
    public void setOnCheckedChangeListener(
        CompoundButton.OnCheckedChangeListener listener){
        if(listener != null) {
            this.myListener = listener;
        }
        myCheckBox.setOnCheckedChangeListener(listener);
    }

    public void silentlySetChecked(boolean checked){
        toggleListener(false);
        myCheckBox.setChecked(checked);
        toggleListener(true);
    }

    private void toggleListener(boolean on){
        if(on) {
            this.setOnCheckedChangeListener(myListener);
        }
        else {
            this.setOnCheckedChangeListener(null);
        }
    }
}

CheckBox仍然可以在XML中声明相同,但在代码中初始化GUI时使用它:

BetterCheckBox myCheckBox;

// later...
myCheckBox = new BetterCheckBox(context,
    (CheckBox) view.findViewById(R.id.my_check_box));

如果您想在不触发听众的情况下从代码设置检查,请拨打myCheckBox.silentlySetChecked(someBoolean)而不是setChecked

答案 1 :(得分:36)

也许你可以检查isShown()?如果为TRUE - 而不是它的用户。适合我。

setOnCheckedChangeListener(new OnCheckedChangeListener() {
    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        if (myCheckBox.isShown()) {// makes sure that this is shown first and user has clicked/dragged it
                  doSometing();
        }
    }
});

答案 2 :(得分:18)

您可以在以编程方式更改之前删除侦听器并再次添加,如下面的SO帖子所示:

https://stackoverflow.com/a/14147300/1666070

public function updateHover(e:MouseEvent):void
{
    var p:Point=pointToXY(e.localX,e.localY);
    _tileHover.visible = false; // hide hover for now
    if ((p.y<0) || (p.y>=tileArray.length)) return;  // range error on Y
    if ((p.x<0)||(p.x>=tileArray[p.y].length)) return; // range error on X
    if (!tileArray[p.y][p.x]) return; // no tile
    var _tile:Tile=tileArray[p.y][p.x];
    _tileHover.x=_tile.x;
    _tileHover.y=_tile.y; // no need to convert xyToPoint() we have coords stored in tile
    _tileHover.visible=true;
}

答案 3 :(得分:14)

在onCheckedChanged()内部,只检查用户是否实际检查/取消选中了单选按钮,然后按如下方式执行相应操作:

mMySwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
 @Override
 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    if (buttonView.isPressed()) {
         // User has clicked check box
        }
   else
       {
         //triggered due to programmatic assignment using 'setChecked()' method.   
         }
}

});

答案 4 :(得分:4)

尝试扩展CheckBox。这样的事情(不是完整的例子):

public MyCheckBox extends CheckBox {

   private Boolean isCheckedProgramatically = false;

   public void setChecked(Boolean checked) {
       isCheckedProgramatically = true;
       super.setChecked(checked);
   }

   public Boolean isNotSetByUser() {
      return isCheckedProgramatically;
   }

}

答案 5 :(得分:3)

另一个简单的解决方案非常有效。示例适用于Switch。

public class BetterSwitch extends Switch {
  //Constructors here...

    private boolean mUserTriggered;

    // Use it in listener to check that listener is triggered by the user.
    public boolean isUserTriggered() {
        return mUserTriggered;
    }

    // Override this method to handle the case where user drags the switch
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        boolean result;

        mUserTriggered = true;
        result = super.onTouchEvent(ev);
        mUserTriggered = false;

        return result;
    }

    // Override this method to handle the case where user clicks the switch
    @Override
    public boolean performClick() {
        boolean result;

        mUserTriggered = true;
        result = super.performClick();
        mUserTriggered = false;

        return result;
    }
}

答案 6 :(得分:2)

有趣的问题。据我所知,一旦你进入监听器,就无法检测到哪个动作触发了监听器,上下文是不够的。除非您使用外部布尔值作为指标。

当您选中“以编程方式”框时,请先设置一个布尔值,以表明它是以编程方式完成的。类似的东西:

private boolean boxWasCheckedProgrammatically = false;

....

// Programmatic change:
boxWasCheckedProgrammatically = true;
checkBoxe.setChecked(true)

在你的听众中,不要忘记重置复选框的状态:

@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    if (isNotSetByUser()) {
        resetBoxCheckSource();
        return;
    }
    doSometing();
}

// in your activity:
public boolean isNotSetByUser() {
    return boxWasCheckedProgrammatically;
}

public void resetBoxCheckedSource() {
    this.boxWasCheckedProgrammatically  = false;
}

答案 7 :(得分:2)

尝试NinjaSwitch

只需拨打setChecked(boolean, true)即可更改未经检测到的交换机已检查状态!

public class NinjaSwitch extends SwitchCompat {

    private OnCheckedChangeListener mCheckedChangeListener;

    public NinjaSwitch(Context context) {
        super(context);
    }

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

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

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

    /**
     * <p>Changes the checked state of this button.</p>
     *
     * @param checked true to check the button, false to uncheck it
     * @param isNinja true to change the state like a Ninja, makes no one knows about the change!
     */
    public void setChecked(boolean checked, boolean isNinja) {
        if (isNinja) {
            super.setOnCheckedChangeListener(null);
        }
        setChecked(checked);
        if (isNinja) {
            super.setOnCheckedChangeListener(mCheckedChangeListener);
        }
    }
}

答案 8 :(得分:2)

如果OnClickListener已设置且不应被覆盖,请使用!buttonView.isPressed()作为isNotSetByUser()

否则,最好的变体是使用OnClickListener而不是OnCheckedChangeListener

答案 9 :(得分:2)

接受的答案可以简化一点,以保持对原始复选框的引用。这使得我们可以直接在XML中使用SilentSwitchCompat(或SilentCheckboxCompat,如果您愿意)。我也做了,所以如果你愿意的话,可以将OnCheckedChangeListener设置为null

public class SilentSwitchCompat extends SwitchCompat {
  private OnCheckedChangeListener listener = null;

  public SilentSwitchCompat(Context context) {
    super(context);
  }

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

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

  /**
   * Check the {@link SilentSwitchCompat}, without calling the {@code onCheckChangeListener}.
   *
   * @param checked whether this {@link SilentSwitchCompat} should be checked or not.
   */
  public void silentlySetChecked(boolean checked) {
    OnCheckedChangeListener tmpListener = listener;
    setOnCheckedChangeListener(null);
    setChecked(checked);
    setOnCheckedChangeListener(tmpListener);
  }
}

然后您可以直接在XML中使用它(注意:您将需要整个包名称):

<com.my.package.name.SilentCheckBox
      android:id="@+id/my_check_box"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:textOff="@string/disabled"
      android:textOn="@string/enabled"/>

然后你可以通过调用以下方式静默检查该框:

SilentCheckBox mySilentCheckBox = (SilentCheckBox) findViewById(R.id.my_check_box)
mySilentCheckBox.silentlySetChecked(someBoolean)

答案 10 :(得分:1)

我的带有Kotlin扩展功能的变体:

fun CheckBox.setCheckedSilently(isChecked: Boolean, onCheckedChangeListener: CompoundButton.OnCheckedChangeListener) {
    if (isChecked == this.isChecked) return
    this.setOnCheckedChangeListener(null)
    this.isChecked = isChecked
    this.setOnCheckedChangeListener(onCheckedChangeListener)
}

...不幸的是,我们每次都需要传递onCheckedChangeListener,因为CheckBox类没有为mOnCheckedChangeListener字段((

用法:

checkbox.setCheckedSilently(true, myCheckboxListener)

答案 11 :(得分:1)

这应该足够了:

SwitchCompact.setOnCheckedChangeListener((buttonView, isChecked) -> {
         if (buttonView.isPressed()) {
            if (!isChecked) {
               //do something
            } else {
              // do something else
            }
         }
      });

答案 12 :(得分:0)

创建变量

boolean setByUser = false;  // Initially it is set programmatically


private void notSetByUser(boolean value) {
   setByUser = value;
}
// If user has changed it will be true, else false 
private boolean isNotSetByUser() {
   return setByUser;          

}

在应用程序中更改而不是用户时,请调用notSetByUser(true)以使其不被用户设置,否则请调用notSetByUser(false),即由程序设置。

最后,在您的事件监听器中,在调用isNotSetByUser()之后,请确保再次将其更改回正常状态。

每当您通过用户或以编程方式处理该操作时,请调用此方法。使用适当的值调用notSetByUser()。

答案 13 :(得分:0)

如果未使用视图标记,则可以使用它而不是扩展复选框:

        checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {

                @Override
                public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
                    if (buttonView.getTag() != null) {
                        buttonView.setTag(null);
                        return;
                    }
                    //handle the checking/unchecking
                    }

每当您调用选中/取消选中复选框的内容时,也请在选中/取消选中之前调用此方法:

checkbox.setTag(true);

答案 14 :(得分:0)

我已经使用RxJava的PublishSubject(简单的扩展名)创建了扩展名。仅对“ OnClick”事件做出反应。

/**
 * Creates ClickListener and sends switch state on each click
 */
fun CompoundButton.onCheckChangedByUser(): PublishSubject<Boolean> {
    val onCheckChangedByUser: PublishSubject<Boolean> = PublishSubject.create()
    setOnClickListener {
        onCheckChangedByUser.onNext(isChecked)
    }
    return onCheckChangedByUser
}