Ubah nilai kotak centang tanpa memicu padaCheckChanged

147

Saya telah setOnCheckedChangeListenermenerapkan untukcheckbox

Apakah ada cara saya bisa menelepon

checkbox.setChecked(false);

tanpa memicu onCheckedChanged

Archie.bpgc
sumber
Mengapa tidak menggunakan flag true / false yang sederhana? Ini cara paling sederhana untuk menyelesaikan masalah ini dan hanya membutuhkan tiga baris kode tambahan. Lihat jawaban saya di bawah ini.
Ruchir Baronia

Jawaban:

280

Tidak, kamu tidak bisa melakukannya. The onCheckedChangedmetode ini disebut langsung dari setChecked. Yang dapat Anda lakukan adalah sebagai berikut:

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

Lihat sumber Kotak Centang , dan penerapan 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;            
    }
}
Naungan
sumber
6
Bagaimana Anda melamarnya mListener? Checkboxtidak memiliki pengambil untuk ituOnCheckChangeListener
tir38
20
Yah tidak perlu downvote hanya karena Anda tidak mengerti solusinya. mListeneradalah implementasi dari OnCheckChangedListenerantarmuka, yang dibuat oleh programmer. Jawaban saya menyiratkan bahwa programmer mempertahankan referensi untuk implementasi mereka sendiri - mListener.
Shade
Apakah tidak efisien untuk mengubah pendengar jika Anda ingin menggunakan metode setChecked () berulang kali?
Ren
4
@ Ren, mengubah pendengar hanya melibatkan pengaturan properti di CheckBoxobjek. Saya tidak akan mengatakan itu tidak efisien.
Shade
Dokumen berbunyi "Dipanggil ketika tombol radio yang diperiksa telah berubah. Ketika pemilihan dihapus, checkId adalah -1". Ini benar-benar menyesatkan, seharusnya isChecked juga dilewati.
AA_PV
69

Tambahkan kode ini di dalam OnCheckedChangeListener:

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

Ini akan membantu kami untuk mengetahui kondisi kotak centang cuaca telah diubah secara programatik atau oleh tindakan pengguna.

krisDrOid
sumber
11
Ini harus ditandai sebagai solusi, berfungsi seperti pesona!
Ashwin Balani
5
Waspadalah! Ini merusak mode aksesibilitas! isPressed () tidak benar ketika dipicu oleh ketukan dua kali dari mode asisten suara.
A1m
21

Cara lain yang mungkin untuk mencapai ini adalah dengan menggunakan Kotak Centang kustom, yang akan memungkinkan Anda memilih apakah Anda ingin pendengar dipanggil atau tidak:

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

Versi Kotlin, jika Anda lebih suka:

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

penggunaan sampel:

checkBox.setChecked(true,false);
pengembang android
sumber
11

Anda hanya menggunakan setonclickListener, ini akan berfungsi dengan baik dan ini adalah metode yang sangat sederhana, terima kasih :)

Rohit
sumber
35
onClick () tidak dipanggil saat pengguna menggeser sakelar.
James
2
Memang, melakukan hal ini membuat Anda tidak memiliki penangan ketika pengguna menyeret sakelar.
Victor G
lalu bagaimana Anda akan mendapatkan nilai isChecked yaitu benar atau salah?
Abhi
6

Menggunakan ekstensi Kotlin dengan jawaban @Shade:

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

Bagi siapa pun yang menemukan ini, satu cara sederhana untuk melakukan ini adalah dengan menggunakan tag pada kotak centang dan kemudian memeriksa tag itu pada pendengarnya (kode ada di Kotlin):

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

Kemudian ketika mengakses cukup atur tag ke true sebelum Anda mengatur isChecked to true ketika Anda ingin mengabaikan perubahan nilai:

checkBox.tag = true
checkBox.isChecked = true

Anda juga bisa memetakan tag ke kunci dengan menggunakan metode setTag alternatif yang membutuhkan kunci jika Anda khawatir tentang pemahaman. Tetapi jika semuanya berisi satu kelas saja, beberapa string komentar akan lebih dari cukup untuk menjelaskan apa yang terjadi.

Chris
sumber
4

Anda bisa menggunakan kelas SafeCheckBox ini sebagai kotak centang Anda:

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);
    }
}
  • Maka Anda dapat menelepon: -

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

Krishan
sumber
3

Setel nol ke changeListener sebelum memeriksa tombol radio. Anda dapat mengatur pendengar lagi setelah memeriksa tombol radio.

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

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

   }
});
gencaysahinn
sumber
1
Melakukan pekerjaan dan itu jauh lebih cepat dan lebih sederhana daripada solusi lain di sini.
PayToPwn
3

Interpretasi saya yang saya pikir paling mudah
Semoga bermanfaat)

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

Gunakan

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

gunakan akan memicu setChecked(boolean)fungsi
itu saja

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
sumber
2

Ini adalah solusi sederhana yang saya gunakan:
Tentukan pendengar khusus:

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

Inisialisasi:

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

Pemakaian:

checkBoxListener.disable();

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

checkBoxListener.enable();
vovahost
sumber
2

Bagaimana dengan ini. Coba gunakan Tag dalam Tampilan

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

dan

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
sumber
2

Saya menggunakan ReentrantLock, dan menguncinya setiap kali saya mengatur 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
sumber
1

Saya menemukan semua jawaban di atas terlalu rumit. Mengapa tidak membuat bendera sendiri dengan boolean sederhana?

Cukup gunakan sistem bendera sederhana dengan boolean. Buat boolean noListener. Kapan pun Anda ingin menghidupkan / mematikan sakelar tanpa menjalankan kode apa pun (dalam contoh ini, direpresentasikan sebagai runListenerCode(), cukup setel noListener=truesebelum memanggilswitch.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;
               }
           });

Solusi yang sangat sederhana menggunakan flag sederhana. Pada akhirnya, kami menetapkan noListener=falsesekali lagi sehingga kode kami terus berfungsi. Semoga ini membantu!

Ruchir Baronia
sumber
1

Coba yang ini seharusnya bekerja untuk Anda! Anda dapat menggunakan ini dengan firebase juga!

Untuk mendapatkan data berbasis firebase! Gunakan ini!

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

Setelah itu ketika pengguna melakukan sesuatu!

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
sumber
1

Saya tidak benar-benar ingin harus melewati pendengar di setiap kali kami mengatur diperiksa berubah, atau menggunakan enabledsebagai cara untuk menentukan apakah kita harus menetapkan nilai (apa yang terjadi dalam kasus kami telah menonaktifkan saklar ketika mengatur nilai ?)

Alih-alih, saya menggunakan tag dengan id dan beberapa metode ekstensi yang dapat Anda hubungi:

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

Sekarang Anda dapat menelepon setCheckedWithoutCallingListener(bool)dan itu akan memberlakukan penggunaan pendengar yang benar.

Anda juga masih dapat menelepon setChecked(bool)untuk memecat pendengar jika Anda masih membutuhkannya

Daentech
sumber
0

Saya kira menggunakan refleksi adalah satu-satunya cara. Sesuatu seperti ini:

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
sumber
Dapat bekerja hingga dev mengubah nama bidang atau, misalnya, menarik metode "isChecked" dalam hierarki ... atau melakukan refactoring lain ... Setidaknya tambahkan sesuatu sepertiif(Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN){ /* do reflection */}
aeracode
Gagasan yang salah adalah mendorong untuk memutus API dan menggali internal. Setiap perubahan pada implementasi akan menyebabkan aplikasi gagal.
mspanc
0

Solusi saya ditulis dalam java berdasarkan jawaban @ 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 kotak centang dan selalu satu akan diperiksa (satu harus awalnya harus diperiksa). Menyetel tag ke blok yang benar di pendengar yang diubah

Makalele
sumber
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
sumber