web-dev-qa-db-ja.com

Switch、Checkboxの値がユーザーによって変更されたか、プログラムによって(保持を含む)変更されたかを区別するにはどうすればよいですか?

_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()の実装方法

97
Solvek

回答2:

非常に簡単な答え:

OnCheckedChangeListenerの代わりにOnClickListenerを使用する

_    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パターンを参照)を作成しました。

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

リスナーをトリガーせずにコードからチェックを設定する場合は、setCheckedの代わりにmyCheckBox.silentlySetChecked(someBoolean)を呼び出します。

146
anthropomo

たぶん、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();
        }
    }
});
37
Denis Babak

次のSO投稿で回答されているように、プログラムで変更する前にリスナーを削除して再度追加できます。

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

theCheck.setOnCheckedChangeListener(null);
theCheck.setChecked(false);
theCheck.setOnCheckedChangeListener(toggleButtonChangeListener);
19
Eli Kohen

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

});

16
adi

CheckBoxを拡張してみてください。そのようなもの(完全な例ではありません):

public MyCheckBox extends CheckBox {

   private Boolean isCheckedProgramatically = false;

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

   public Boolean isNotSetByUser() {
      return isCheckedProgramatically;
   }

}
4
Romain Piel

かなりうまく機能する別の簡単なソリューションがあります。例はスイッチ用です。

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;
    }
}
3
Invised

興味深い質問。私の知る限り、リスナに入ったら、どのアクションがリスナをトリガーしたかを検出できません。コンテキストは十分ではありません。外部ブール値をインジケータとして使用しない限り。

「プログラムで」ボックスにチェックマークを付けるとき、前にブール値を設定して、プログラムで行われたことを示します。何かのようなもの:

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;
}
2
Guillaume

OnClickListenerがすでに設定されており、上書きしない場合は、!buttonView.isPressed()isNotSetByUser()として使用します。

それ以外の場合、OnClickListenerの代わりにOnCheckedChangeListenerを使用するのが最善の方法です。

2
Pavel

元のチェックボックスへの参照を維持しないように、受け入れられた答えを少し単純化できます。これにより、SilentSwitchCompat(または必要に応じてSilentCheckboxCompat)をXMLで直接使用できるようになります。また、必要に応じてOnCheckedChangeListenernullに設定できるように作成しました。

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)
2
Smalls

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);
        }
    }
}
2
Taeho Kim

これが私の実装です

カスタムスイッチのJavaコード:

public class CustomSwitch extends SwitchCompat {

private OnCheckedChangeListener mListener = null;

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

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

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

@Override
public void setOnCheckedChangeListener(@Nullable OnCheckedChangeListener listener) {
    if(listener != null && this.mListener != listener) {
        this.mListener = listener;
    }
    super.setOnCheckedChangeListener(listener);
}

public void setCheckedSilently(boolean checked){
    this.setOnCheckedChangeListener(null);
    this.setChecked(checked);
    this.setOnCheckedChangeListener(mListener);
}}

同等のコトリンコード:

class CustomSwitch : SwitchCompat {

private var mListener: CompoundButton.OnCheckedChangeListener? = null

constructor(context: Context) : super(context) {}

constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {}

constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}

override fun setOnCheckedChangeListener(@Nullable listener: CompoundButton.OnCheckedChangeListener?) {
    if (listener != null && this.mListener != listener) {
        this.mListener = listener
    }
    super.setOnCheckedChangeListener(listener)
}

fun setCheckedSilently(checked: Boolean) {
    this.setOnCheckedChangeListener(null)
    this.isChecked = checked
    this.setOnCheckedChangeListener(mListener)
}}

リスナーを使用せずにスイッチの状態を変更するには:

swSelection.setCheckedSilently(contact.isSelected)

通常どおり、状態の変化を監視できます:

swSelection.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
   @Override
   public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
      // Do something
   }       
 });

コトリンで:

 swSelection.setOnCheckedChangeListener{buttonView, isChecked -> run {
            contact.isSelected = isChecked
        }}
1
thilina Kj

Kotlin拡張機能を持つ私のバリアント:

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

...残念ながら、CheckBoxクラスにはmOnCheckedChangeListenerフィールド((

使用法:

checkbox.setCheckedSilently(true, myCheckboxListener)
1
Fedir Tsapana

変数を作成する

_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()を呼び出します。

0
Tvd

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
}
0
Janusz Hain

ビューのタグが使用されていない場合は、チェックボックスを拡張する代わりに使用できます:

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