Switch, Checkbox 값이 사용자 또는 프로그래밍 방식 (보존 포함)에 의해 변경되었는지 어떻게 구별 할 수 있습니까?


110
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()?


확실하지는 않지만 사용자가 토글하면 해당 리스너를 설정하면 onClick 콜백도 받게 될 것이라고 생각합니다. 따라서 onClick에서 부울 플래그를 설정할 수 있지만 onCheckChanged에서 확인하여 사용자가 변경을 시작했는지 확인할 수 있습니다.
FoamyGuy 2012


더 간단하고 명확한 솔루션이 있습니다. stackoverflow.com/a/41574200/3256989
ultraon

답변:


157

답변 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 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.


15
의 경우 Switch답변 1은 탭 케이스와 슬라이드 케이스 모두에서 작동하는 반면 답변 2는 탭 케이스에서만 작동합니다. 개인적인 취향, 나는 내 수업 연장했다 CheckBox/ Switch오히려 하나에 대한 참조를 잡고보다. 그런 식으로 더 깔끔하게 느껴집니다 (그렇게하려면 XML에 전체 패키지 이름을 지정해야합니다).
howettl

1
이 anthropomo를 주셔서 감사합니다. 왜 일찍 생각하지 않았는지 모르겠지만 소중한 시간을 절약했습니다.). 건배!
CyberDandy

나는 이것에 대해 확신하지 못하지만, Material Design 스위치를 얻기 위해 SwitchCompat를 확장하면 (appcompat v7 사용), 새로운 재 설계 및 착색 기능을 종료 할 수 있습니다.
DNax 2014

4
두 솔루션 모두 심각한 결함이 있습니다. 첫 번째 해결책 : 사용자가 스위치를 드래그 할 때 리스너가 실행되지 않습니다. 두 번째 솔루션 : setOnCheckedChangeListener는 null 검사를 수행하기 때문에 이미 새 항목이 설정되어있을 때 실제로 이전 수신기를 설정합니다.
Paul Woitaschek

첫 번째 해결책 : 간결한 해결책을 원하고 해당 위젯을 사용하지 않으려는 경우 알려진 문제이며 반 허용되는 문제입니다. 두 번째 솔루션 : 그런 종류의 가능성이없는 경우를 해결하기 위해 null 검사를 변경했습니다. 이제 null이 아닐 때마다 할당 listener합니다 . 거의 모든 경우에 이것은 아무 일도하지 않지만, 어떤 이유로 든 새로운 청취자를 만든다면 그것은 깨지지 않습니다. myListenerlistener
anthropomo

38

isShown ()을 확인할 수 있습니까? 참인 경우-사용자보다. 나를 위해 작동합니다.

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

1
onStart () 또는 onResume ()에서 "setChecked (isChecked)"를 호출하더라도 작동합니다 (예기치 않은 콜백이 없음을 의미). 따라서 완벽한 솔루션으로 간주 될 수 있습니다.
Alan Zhiliang Feng

1
일반적인 해결책이 아닌 것 같습니다. 버튼이 순간에 표시되지만 값이 코드에서 변경되면 어떻게됩니까?
Pavel

1
이해가 안 돼요. 'isShown ()'이 사용자 작업과 프로그래밍 방식 변경을 어떻게 구분하나요? 'isShown ()'이 참이면 어떻게 사용자 액션이라고 말할 수 있습니까?
모하메드 Refaat

1
이 솔루션은 매우 특정한 경우에만 작동하며 문서화되지 않은 미 보장 활동 생성 및 레이아웃 프로세스에 의존합니다. 이것이 미래에 중단되지 않을 것이라는 보장은 없으며 뷰가 이미 렌더링 된 경우 작동하지 않습니다.
Manuel

26

내부에서 onCheckedChanged()사용자가 실제로 checked/unchecked라디오 버튼을 가지고 있는지 확인한 다음 그에 따라 다음과 같이 작업하십시오.

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

좋은 솔루션,보기를 사용자 지정할 필요가 없습니다.
Aju

사랑해. : DDD
Vlad

12
사용자가 와이프 / 슬라이딩 스위치를 전환하는 경우에는이 작동하지 않는 경우
mohitum

1
이 접근 방식을 모든 곳에서 사용했지만 isPressed음성 안내 지원이 켜져있는 Nokia 기기에서 false 로 작동하지 않는 경우를 발견했습니다 .
Mikhail

SwitchMaterial은 문제없이 작동합니다. 좋은 결정, 감사합니다!
R00We

25

프로그래밍 방식으로 변경하기 전에 리스너를 제거하고 다음 SO 게시물에 답변 한대로 다시 추가 할 수 있습니다.

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

theCheck.setOnCheckedChangeListener(null);
theCheck.setChecked(false);
theCheck.setOnCheckedChangeListener(toggleButtonChangeListener);

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

}

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

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

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

2

경우는 OnClickListener이미 설정되어 있고, 사용 덮어 안 !buttonView.isPressed()등을 isNotSetByUser().

그렇지 않으면 최고 변형 사용하는 OnClickListener대신 OnCheckedChangeListener.


buttonView.isPressed ()는 좋은 솔루션입니다. OnClickListener를 사용할 때 문제가 있습니다. 사용자가 스위치를 눌렀을 때 콜백을받지 못합니다.
Aju

2

원래 확인란에 대한 참조를 유지하지 않기 위해 수락 된 답변을 약간 단순화 할 수 있습니다. 이렇게하면 XML에서 직접 SilentSwitchCompat(또는 SilentCheckboxCompat원하는 경우) 사용할 수 있습니다 . 당신이 설정할 수 있도록 나는 또한 그것을 만든 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)

1

내 구현은 다음과 같습니다.

사용자 지정 스위치 용 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);
}}

동등한 Kotlin 코드 :

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

Kotlin에서 :

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

1

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 필드에 대한 getter가 없기 때문에 매번 onCheckedChangeListener를 전달해야합니다 ((

용법:

checkbox.setCheckedSilently(true, myCheckboxListener)

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 ()를 호출하십시오.


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

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
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.