_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()
の実装方法
回答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)
を呼び出します。
たぶん、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();
}
}
});
次のSO投稿で回答されているように、プログラムで変更する前にリスナーを削除して再度追加できます。
https://stackoverflow.com/a/14147300/166607
theCheck.setOnCheckedChangeListener(null);
theCheck.setChecked(false);
theCheck.setOnCheckedChangeListener(toggleButtonChangeListener);
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.
}
}
});
CheckBoxを拡張してみてください。そのようなもの(完全な例ではありません):
public MyCheckBox extends CheckBox {
private Boolean isCheckedProgramatically = false;
public void setChecked(Boolean checked) {
isCheckedProgramatically = true;
super.setChecked(checked);
}
public Boolean isNotSetByUser() {
return isCheckedProgramatically;
}
}
かなりうまく機能する別の簡単なソリューションがあります。例はスイッチ用です。
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;
}
}
興味深い質問。私の知る限り、リスナに入ったら、どのアクションがリスナをトリガーしたかを検出できません。コンテキストは十分ではありません。外部ブール値をインジケータとして使用しない限り。
「プログラムで」ボックスにチェックマークを付けるとき、前にブール値を設定して、プログラムで行われたことを示します。何かのようなもの:
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;
}
OnClickListener
がすでに設定されており、上書きしない場合は、!buttonView.isPressed()
をisNotSetByUser()
として使用します。
それ以外の場合、OnClickListener
の代わりにOnCheckedChangeListener
を使用するのが最善の方法です。
元のチェックボックスへの参照を維持しないように、受け入れられた答えを少し単純化できます。これにより、SilentSwitchCompat
(または必要に応じてSilentCheckboxCompat
)をXMLで直接使用できるようになります。また、必要に応じて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)
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);
}
}
}
これが私の実装です
カスタムスイッチの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
}}
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)
変数を作成する
_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()を呼び出します。
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
}
ビューのタグが使用されていない場合は、チェックボックスを拡張する代わりに使用できます:
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);