Ändern Sie den Checkbox-Wert, ohne onCheckChanged auszulösen

147

Ich habe setOnCheckedChangeListenerfür meine implementiertcheckbox

Gibt es einen Weg, den ich anrufen kann?

checkbox.setChecked(false);

ohne die auszulösen onCheckedChanged

Archie.bpgc
quelle
Warum nicht einfach ein einfaches True / False-Flag verwenden? Dies ist der einfachste Weg, um dieses Problem zu lösen, und es werden nur drei Zeilen zusätzlichen Codes benötigt. Siehe meine Antwort unten.
Ruchir Baronia

Antworten:

279

Nein, das kannst du nicht. Die onCheckedChangedMethode wird direkt von aufgerufen setChecked. Was Sie tun können, ist Folgendes:

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

Siehe die Quelle von CheckBox und die Implementierung von 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;            
    }
}
Schatten
quelle
6
Wie schlagen Sie vor, zu bekommen mListener? Checkboxhat keinen Getter für seineOnCheckChangeListener
tir38
20
Sie müssen nicht einfach abstimmen, weil Sie die Lösung nicht verstehen. mListenerist eine Implementierung der OnCheckChangedListenerSchnittstelle, die vom Programmierer erstellt wurde. Meine Antwort impliziert, dass der Programmierer einen Verweis auf seine eigene Implementierung beibehalten hat - mListener.
Schatten
Wäre es ineffizient, den Listener zu ändern, wenn Sie die setChecked () -Methode wiederholt verwenden möchten?
Ren
4
@Ren, das Ändern des Listeners beinhaltet nur das Festlegen einer Eigenschaft im CheckBoxObjekt. Ich würde nicht sagen, dass das ineffizient ist.
Schatten
Das Dokument lautet "Wird aufgerufen, wenn sich das aktivierte Optionsfeld geändert hat. Wenn die Auswahl gelöscht ist, ist" CheckedId "-1". Dies ist wirklich irreführend. Entweder sollte auch das isChecked durchlaufen werden.
AA_PV
69

Fügen Sie diesen Code in OnCheckedChangeListener hinzu:

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

Dies hilft uns herauszufinden, ob der Status der Wetterkontrollbox programmgesteuert oder durch Benutzeraktion geändert wurde.

krisDrOid
quelle
11
Dies sollte als Lösung markiert werden, funktioniert wie ein Zauber!
Ashwin Balani
5
sei vorsichtig! Dies unterbricht den Eingabehilfenmodus! isPressed () ist nicht wahr, wenn es durch zweimaliges Tippen aus dem Sprachassistentenmodus ausgelöst wird.
A1m
21

Eine andere Möglichkeit, dies zu erreichen, ist die Verwendung einer benutzerdefinierten CheckBox, mit der Sie auswählen können, ob der Listener aufgerufen werden soll oder nicht:

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-Version, wenn Sie es vorziehen:

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

Beispielnutzung:

checkBox.setChecked(true,false);
Android-Entwickler
quelle
11

Sie verwenden einfach setonclickListener, es wird gut funktionieren und dies ist eine sehr einfache Methode, danke :)

Rohit
quelle
35
onClick () wird nicht aufgerufen, wenn der Benutzer den Schalter drückt.
James
2
Wenn Sie dies tun, haben Sie keinen Handler mehr, wenn der Benutzer den Umschalter zieht.
Victor G
Wie erhalten Sie dann den geprüften Wert, dh wahr oder falsch?
Abhi
6

Verwenden von Kotlins Erweiterungen mit @Shade Antwort:

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

Für alle, die darauf stoßen, besteht eine einfachere Möglichkeit darin, einfach ein Tag im Kontrollkästchen zu verwenden und dieses Tag dann im Listener zu aktivieren (Code befindet sich in Kotlin):

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

Setzen Sie dann beim Zugriff einfach das Tag auf true, bevor Sie isChecked auf true setzen, wenn Sie die Wertänderung ignorieren möchten:

checkBox.tag = true
checkBox.isChecked = true

Sie können das Tag auch einem Schlüssel zuordnen, indem Sie die alternative setTag-Methode verwenden, für die ein Schlüssel erforderlich ist, wenn Sie Bedenken hinsichtlich der Verständlichkeit haben. Aber wenn alles in einer einzelnen Klasse enthalten ist, sind ein paar Kommentarzeichenfolgen mehr als genug, um zu erklären, was passiert.

Chris
quelle
4

Sie können diese SafeCheckBox-Klasse als Kontrollkästchen verwenden:

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);
    }
}
  • Dann könnten Sie anrufen: -

    setSafeCheck(true,ListenerMode.IGNORE);// OnCheckedChange listener will not be notified

krishan
quelle
3

Setzen Sie null auf changeListener, bevor Sie das Optionsfeld aktivieren. Sie können den Listener erneut einstellen, nachdem Sie das Optionsfeld aktiviert haben.

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

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

   }
});
Gencaysahinn
quelle
1
Erledigt die Arbeit und ist viel schneller und einfacher als die anderen Lösungen hier.
PayToPwn
3

Meine Interpretation, die ich für die einfachste halte,
kann hilfreich sein.

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

benutze es

@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean on) {
    if (((ProgrammableSwitchCompat) compoundButton).isCheckedProgrammatically) {
        return;
    }
    //...
    ((ProgrammableSwitchCompat) compoundButton).setCheckedProgrammatically(true);
    //...
    ((ProgrammableSwitchCompat) compoundButton).setCheckedProgrammatically(false);
    //...
}

Mit use wird eine setChecked(boolean)Funktion ausgelöst
, die alles ist

KOTLIN

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)
    }
}
Vlad
quelle
2

Dies ist eine einfache Lösung, die ich verwendet habe:
Definieren Sie einen benutzerdefinierten Listener:

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

Initialisierung:

CompoundButtonListener checkBoxListener = new CompoundButtonListener() {
    @Override
    public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
        if (isEnabled()) {
            // Your code goes here
        }
    }
};
myCheckBox.setOnCheckedChangeListener(checkBoxListener);

Verwendung:

checkBoxListener.disable();

// Some logic based on which you will modify CheckBox state
// Example: myCheckBox.setChecked(true)

checkBoxListener.enable();
vovahost
quelle
2

Wie wäre es damit. Versuchen Sie, Tag in View zu verwenden

mCheck.setTag("ignore");
mCheck.setChecked(true);
mCheck.setTag(null);

und

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
            }

        }
    });
Areema
quelle
2

Ich habe a verwendet ReentrantLockund es gesperrt, wenn ich Folgendes einstelle 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
    }
}
Eric
quelle
1

Ich fand alle obigen Antworten viel zu kompliziert. Warum erstellen Sie nicht einfach Ihre eigene Flagge mit einem einfachen Booleschen Wert?

Verwenden Sie einfach ein einfaches Flag-System mit einem Booleschen Wert. Erstellen boolean noListener. Wann immer Sie Ihren Schalter ein- und ausschalten möchten, ohne Code auszuführen (in diesem Beispiel dargestellt als runListenerCode(), einfach noListener=truevor dem Aufruf festgelegtswitch.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;
               }
           });

Sehr einfache Lösung mit einfachen Flags. Am Ende setzen wir noListener=falsenoch einmal, damit unser Code weiter funktioniert. Hoffe das hilft!

Ruchir Baronia
quelle
1

Versuchen Sie dieses sollte für Sie arbeiten! Sie können dies auch mit Firebase verwenden!

Um Firebase-Daten zu erhalten! Benutze das!

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

Danach, wenn Benutzer etwas tun!

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
                    }
                }
            }
        });
Lakpriya Senevirathna
quelle
1

Ich wollte nicht wirklich den Listener jedes Mal übergeben müssen, wenn wir das Kontrollkästchen geändert oder verwendet haben enabled , um zu bestimmen, ob wir den Wert einstellen sollen (was passiert, wenn der Schalter bereits beim Einstellen des Werts deaktiviert ist ?)

Stattdessen verwende ich Tags mit einer ID und einigen Erweiterungsmethoden, die Sie aufrufen können:

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

Jetzt können Sie anrufen setCheckedWithoutCallingListener(bool)und die korrekte Verwendung des Listeners erzwingen.

Sie können auch weiterhin anrufen setChecked(bool), um den Listener zu feuern, wenn Sie ihn noch benötigen

daentech
quelle
0

Ich denke, Reflexion ist der einzige Weg. Etwas wie das:

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();
}
Zielony
quelle
Kann funktionieren, bis Entwickler den Feldnamen ändern oder z. B. die "isChecked" -Methode in der Hierarchie aufrufen ... oder ein anderes Refactoring durchführen ... if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN){ /* do reflection */}
Fügen Sie
Es ist eine falsche Idee, zu ermutigen, die API zu brechen und sich mit Interna zu befassen. Jede Änderung an der Implementierung führt dazu, dass Apps fehlschlagen.
mspanc
0

Meine in Java geschriebene Lösung basiert auf der Antwort von @Chris:

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 Kontrollkästchen und immer eines wird aktiviert (eines muss jedoch zunächst aktiviert werden). Setzen des Tags auf true blockiert onCheckedChanged Listener.

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

}
Codebyjames
quelle