onCheckChanged를 트리거하지 않고 Checkbox 값 변경


147

나는 setOnCheckedChangeListener내 구현checkbox

전화 할 수있는 방법이 있습니까

checkbox.setChecked(false);

트리거하지 않고 onCheckedChanged


왜 단순한 true / false 플래그를 사용하지 않습니까? 이 문제를 해결하는 가장 간단한 방법이며 세 줄의 추가 코드 만 필요합니다. 아래 답변을 참조하십시오.
Ruchir Baronia 1

답변:


280

아니요, 할 수 없습니다. 이 onCheckedChanged메소드는에서 직접 호출됩니다 setChecked. 할 수있는 일은 다음과 같습니다.

mCheck.setOnCheckedChangeListener (null);
mCheck.setChecked (false);
mCheck.setOnCheckedChangeListener (mListener);

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

6
당신은 어떻게 제안 mListener합니까? Checkbox게터가 없습니다OnCheckChangeListener
tir38

20
솔루션을 이해하지 못하기 때문에 단순히 공감할 필요가 없습니다. 프로그래머가 만든 인터페이스 mListener의 구현입니다 OnCheckChangedListener. 내 대답은 프로그래머가에 대한 참조를 유지한다는 것을 의미 자신의 구현을 - mListener.
Shade

setChecked () 메소드를 반복적으로 사용하려면 리스너를 변경하는 것이 비효율적입니까?
Ren

4
@Ren, 리스너 변경은 CheckBox객체 의 속성 설정 만 포함 합니다. 나는 그것이 비효율적이라고 말하지 않을 것입니다.
그늘

문서는 "선택된 단일 선택 단추가 변경되면 호출됩니다. 선택을 지우면 checkedId는 -1"입니다. 이것은 실제로 오해의 소지가 있으므로 isChecked도 통과해야합니다.
AA_PV

69

OnCheckedChangeListener 안에이 코드를 추가하십시오 :

if(!compoundButton.isPressed()) {
            return;
}

이것은 프로그램 적으로 또는 사용자 조치에 의해 날씨 checkBox 상태가 변경되었음을 알아내는 데 도움이됩니다.


11
이것은 해결책으로 표시되어야하며 매력처럼 작동합니다!
Ashwin Balani

5
알고! 접근성 모드가 중단됩니다! 음성 도우미 모드에서 두 번 탭하면 isPressed ()가 true가 아닙니다.
A1m 19 1

21

이를 가능하게하는 또 다른 방법은 커스텀 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);
        }
        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)
        }
        super.toggle()
    }
}

샘플 사용법 :

checkBox.setChecked(true,false);

11

당신은 단순히 setonclickListener 사용하면 잘 작동하며 이것은 매우 간단한 방법입니다. 감사합니다 :)


35
사용자가 스위치를 밀면 onClick ()이 호출되지 않습니다.
James

2
실제로, 이렇게하면 사용자가 토글을 끌 때 핸들러가 없어집니다.
Victor G

그렇다면 isChecked 값을 얻는 방법, 즉 참 또는 거짓?
Abhi

6

@Shade answer와 함께 Kotlin의 확장 기능 사용 :

fun CompoundButton.setCustomChecked(value: Boolean,listener: CompoundButton.OnCheckedChangeListener) {
     setOnCheckedChangeListener(null)
     isChecked = value
     setOnCheckedChangeListener(listener)
}

6

이 문제를 우연히 발견하는 사람에게는 확인란에서 태그를 사용한 다음 해당 수신기에서 해당 태그를 확인하는 것입니다 (코드는 Kotlin에 있음).

checkBox.tag = false
checkBox.setOnCheckedChangeListener{ buttonView, isChecked -> 
    if(checkBox.tag != true) {
        //Do some stuff
    } else {
        checkBox.tag = false
    }

그런 다음 액세스 할 때 값 변경을 무시하려는 경우 isChecked를 true로 설정하기 전에 태그를 true로 설정하십시오.

checkBox.tag = true
checkBox.isChecked = true

이해가 걱정되는 경우 키가 필요한 대체 setTag 메소드를 사용하여 태그를 키에 맵핑 할 수도 있습니다. 그러나 모든 것이 단일 클래스에 포함되어 있으면 몇 가지 주석 문자열로 인해 발생하는 상황을 설명하기에 충분합니다.


4

이 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


3

확인 라디오 단추 전에 changeListener로 널을 설정하십시오. 확인 후 라디오 버튼을 리스너를 다시 설정할 수 있습니다.

radioGroup.setOnCheckedChangeListener(null);
radioGroup.check(R.id.radioButton);
radioGroup.setOnCheckedChangeListener(new 

RadioGroup.OnCheckedChangeListener() {
   @Override
   public void onCheckedChanged(RadioGroup radioGroup, @IdRes int i) {

   }
});

1
작업을 수행하고 다른 솔루션보다 빠르고 간단합니다.
PayToPwn

3

내가 생각하는 가장 쉬운 해석은
도움이 될 수 있습니다)

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

2

이것은 내가 사용한 간단한 솔루션입니다
. 사용자 정의 리스너를 정의하십시오.

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

2

이건 어때요. 보기에서 태그를 사용해보십시오.

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
            }

        }
    });

2

을 사용하고 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
    }
}

1

위의 모든 답변이 너무 복잡하다는 것을 알았습니다. 간단한 부울로 자신 만의 깃발을 만드는 것이 어떻습니까?

부울과 함께 간단한 플래그 시스템을 사용하십시오. 을 만듭니다 boolean noListener. 코드를 실행하지 않고 스위치를 켜거나 끄고 싶을 때마다 (이 예제에서는로 표시 하기 전에 runListenerCode()간단히 설정)noListener=trueswitch.setChecked(false/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코드가 계속 작동하도록 다시 설정 했습니다. 도움이 되었기를 바랍니다!


1

이 것이 당신을 위해 작동해야합니다! 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
                    }
                }
            }
        });

1

체크 변경을 설정할 때마다 리스너를 전달하고 싶거나 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)여전히 필요한 경우 리스너를 호출 하기 위해 계속 호출 할 수 있습니다.


0

반사를 사용하는 것이 유일한 방법이라고 생각합니다. 이 같은:

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

DEVS 변화 필드 이름이나, 예를 들면까지 작동 할 수 적어도 추가 뭔가처럼 ... 방법 계층 구조 "의 IsChecked"올려 ... 또는 다른 리팩토링을if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN){ /* do reflection */}
aeracode

API를 깨고 내부를 파헤 치도록 장려하는 것은 잘못된 생각입니다. 구현이 변경되면 앱이 실패합니다.
mspanc

0

@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 개의 체크 박스와 항상 1 개의 체크 박스가 체크됩니다 (하나는 처음에 체크해야합니다). 태그를 true로 설정하면 onCheckedChanged 리스너가 차단됩니다.


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

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