เปลี่ยนค่าช่องทำเครื่องหมายโดยไม่ต้องเรียกใช้ onCheckChanged


147

ฉันsetOnCheckedChangeListenerใช้งานไปแล้วcheckbox

มีวิธีที่ฉันสามารถโทร

checkbox.setChecked(false);

โดยไม่ต้องเรียก onCheckedChanged


ทำไมไม่เพียงแค่ใช้ธงจริง / เท็จง่าย ๆ ? มันเป็นวิธีที่ง่ายที่สุดในการแก้ไขปัญหานี้และใช้โค้ดพิเศษสามบรรทัดเท่านั้น ดูคำตอบของฉันด้านล่าง
Ruchir Baronia

คำตอบ:


280

ไม่คุณไม่สามารถทำได้ วิธีการที่เรียกว่าโดยตรงจากonCheckedChanged setCheckedสิ่งที่คุณสามารถทำได้มีดังต่อไปนี้:

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

ดูแหล่งที่มาของกล่องกาเครื่องหมายและการใช้งานของ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
@ เรนการเปลี่ยนฟังเกี่ยวข้องกับการตั้งค่าของคุณสมบัติในCheckBoxวัตถุเท่านั้น ฉันจะไม่บอกว่ามันไม่มีประสิทธิภาพ
Shade

เอกสารจะอ่าน "เรียกว่าเมื่อกดปุ่มตัวเลือกที่ตรวจสอบแล้วเมื่อมีการล้างข้อมูลส่วนที่เลือกไว้ นี่เป็นสิ่งที่ทำให้เข้าใจผิดจริงๆควรมีการตรวจสอบ isChecked ผ่านเช่นกัน
AA_PV

69

เพิ่มรหัสนี้ภายใน OnCheckedChangeListener:

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

สิ่งนี้จะช่วยให้เราทราบว่าสถานะช่องทำเครื่องหมายสภาพอากาศเปลี่ยนแปลงไปโดยทางโปรแกรมหรือจากการกระทำของผู้ใช้


11
ควรทำเครื่องหมายเป็นวิธีแก้ปัญหาใช้งานได้ดี!
Ashwin Balani

5
ระวัง! นี่เป็นการแบ่งโหมดการช่วยสำหรับการเข้าถึง! isPressed () ไม่เป็นความจริงเมื่อถูกทริกเกอร์ด้วยการแตะสองครั้งจากโหมดผู้ช่วยเสียง
A1m

21

วิธีที่เป็นไปได้อีกวิธีหนึ่งในการบรรลุเป้าหมายนี้คือการใช้กล่องกาเครื่องหมายที่กำหนดเองซึ่งจะช่วยให้คุณเลือกหากคุณต้องการให้ผู้ฟังเรียกหรือไม่:

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

ใช้ส่วนขยายของ Kotlin กับคำตอบ @Shade:

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 เป็นจริงเมื่อคุณต้องการละเว้นการเปลี่ยนแปลงค่า:

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

ตั้งค่าเป็น null เพื่อเปลี่ยนฟังก่อนตรวจสอบปุ่มตัวเลือก คุณสามารถตั้งค่าผู้ฟังอีกครั้งหลังจากตรวจสอบปุ่มตัวเลือก

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)ฟังก์ชั่น
ที่มีทั้งหมด

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

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=trueก่อนโทรswitch.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" ในลำดับชั้น ... หรือทำ refactoring อื่น ... อย่างน้อยก็เพิ่มบางอย่างเช่นif(Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN){ /* do reflection */}
aeracode

เป็นความคิดที่ผิดที่จะสนับสนุนให้แบ่ง API และเจาะลึกเข้าไปใน internals การเปลี่ยนแปลงใด ๆ ในการนำไปปฏิบัติจะทำให้แอปพลิเคชันล้มเหลว
mspanc

0

โซลูชันของฉันเขียนด้วยภาษาจาวาตามคำตอบ @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 ช่องทำเครื่องหมายและจะมีการตรวจสอบหนึ่งช่องเสมอ (จะต้องตรวจสอบตั้งแต่แรก) การตั้งค่าแท็กเป็นบล็อกจริงบนฟังตรวจสอบการเปลี่ยนแปลง


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.