Thay đổi giá trị Hộp kiểm mà không kích hoạt onCheckChanged


147

Tôi đã setOnCheckedChangeListenerthực hiện chocheckbox

Có cách nào tôi có thể gọi

checkbox.setChecked(false);

mà không kích hoạt onCheckedChanged


Tại sao không chỉ sử dụng một cờ đúng / sai đơn giản? Đây là cách đơn giản nhất để giải quyết vấn đề này và nó chỉ mất ba dòng mã bổ sung. Xem câu trả lời của tôi dưới đây.
Ruchir Baronia

Câu trả lời:


279

Không, bạn không thể làm điều đó. Các onCheckedChangedphương pháp được gọi là trực tiếp từ setChecked. Những gì bạn có thể làm là như sau:

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

Xem nguồn của CheckBox và cách triển khai 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
Làm thế nào để bạn đề xuất để có được mListener? Checkboxkhông có một getter cho nóOnCheckChangeListener
tir38

20
Vâng, không cần phải downvote đơn giản vì bạn không hiểu giải pháp. mListenerlà một triển khai của OnCheckChangedListenergiao diện, được tạo ra bởi các lập trình viên. Câu trả lời của tôi ngụ ý rằng lập trình viên duy trì một tham chiếu đến việc thực hiện của riêng họ - mListener.
Bóng râm

Sẽ không hiệu quả khi thay đổi người nghe nếu bạn muốn sử dụng phương thức setChecked () lặp đi lặp lại?
Ren

4
@Ren, thay đổi người nghe chỉ liên quan đến việc thiết lập một thuộc tính trong CheckBoxđối tượng. Tôi sẽ không nói rằng đó là không hiệu quả.
Bóng râm

Tài liệu ghi "Được gọi khi nút radio đã kiểm tra thay đổi. Khi lựa chọn bị xóa, checkId là -1". Điều này thực sự sai lệch, nó cũng nên có isChecked đi qua.
AA_PV

69

Thêm mã này vào OnCheckedChangeListener:

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

Điều này sẽ giúp chúng tôi tìm ra trạng thái kiểm tra thời tiết đã được thay đổi theo chương trình hoặc do hành động của người dùng.


11
Điều này nên được đánh dấu là một giải pháp, hoạt động như một nét duyên dáng!
Ashwin Balani

5
cảnh giác Điều này phá vỡ chế độ tiếp cận! isPression () không đúng khi được kích hoạt bằng cách chạm hai lần từ chế độ trợ lý giọng nói.
A1m

21

Một cách khác có thể để đạt được điều này là bằng cách sử dụng một CheckBox tùy chỉnh, nó sẽ cho phép bạn chọn nếu bạn muốn người nghe được gọi hay không:

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

Phiên bản Kotlin, nếu bạn thích:

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

sử dụng mẫu:

checkBox.setChecked(true,false);

11

bạn sử dụng đơn giản là setonclickListener, nó sẽ hoạt động tốt và đây là phương pháp rất đơn giản, cảm ơn :)


35
onClick () không được gọi khi người dùng trượt công tắc.
James

2
Thật vậy, làm điều này khiến bạn không có trình xử lý khi người dùng kéo nút chuyển đổi.
Victor G

Sau đó, làm thế nào bạn sẽ nhận được giá trị isChecked tức là đúng hay sai?
Abhi

6

Sử dụng các tiện ích mở rộng của Kotlin với câu trả lời @Shade:

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

6

Đối với bất kỳ ai tình cờ phát hiện ra điều này, một cách đơn giản hơn để làm điều này là chỉ sử dụng một thẻ trên hộp kiểm và sau đó kiểm tra thẻ đó trên trình nghe của nó (mã nằm trong Kotlin):

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

Sau đó, khi truy cập, chỉ cần đặt thẻ thành true trước khi bạn đặt isChecked thành true khi bạn muốn bỏ qua thay đổi giá trị:

checkBox.tag = true
checkBox.isChecked = true

Bạn cũng có thể ánh xạ thẻ tới một khóa bằng cách sử dụng phương thức setTag thay thế yêu cầu khóa nếu bạn lo lắng về tính dễ hiểu. Nhưng nếu tất cả chỉ chứa trong một lớp duy nhất, một vài chuỗi nhận xét sẽ là quá đủ để giải thích những gì đang xảy ra.


4

Bạn có thể sử dụng lớp SafeCheckBox này làm hộp kiểm của mình:

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);
    }
}
  • Sau đó, bạn có thể gọi: -

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


3

Đặt null thành changeListener trước khi kiểm tra nút radio. Bạn có thể thiết lập lại trình nghe sau khi kiểm tra nút radio.

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

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

   }
});

1
Liệu công việc và nó nhanh hơn và đơn giản hơn các giải pháp khác ở đây.
PayToPwn

3

Giải thích của tôi mà tôi nghĩ là dễ nhất
có thể hữu ích)

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

sử dụng nó

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

sử dụng sẽ kích hoạt setChecked(boolean)chức năng
đó là tất cả

KOTLINE

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

Đây là một giải pháp đơn giản tôi đã sử dụng:
Xác định trình nghe tùy chỉnh:

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

Khởi tạo:

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

Sử dụng:

checkBoxListener.disable();

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

checkBoxListener.enable();

2

Còn cái này thì sao. Hãy thử sử dụng Thẻ trong Chế độ xem

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

Tôi đã sử dụng ReentrantLockvà khóa nó bất cứ khi nào tôi cài đặt 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

Tôi thấy tất cả các câu trả lời trên quá phức tạp. Tại sao không chỉ tạo cờ của riêng bạn với một boolean đơn giản?

Chỉ cần sử dụng một hệ thống cờ đơn giản với một boolean. Tạo boolean noListener. Bất cứ khi nào bạn muốn bật / tắt công tắc mà không chạy bất kỳ mã nào (trong ví dụ này, được biểu thị là runListenerCode(), chỉ cần đặt noListener=truetrước khi gọiswitch.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;
               }
           });

Giải pháp rất đơn giản sử dụng cờ đơn giản. Cuối cùng, chúng tôi thiết lập noListener=falsemột lần nữa để mã của chúng tôi tiếp tục hoạt động. Hi vọng điêu nay co ich!


1

Hãy thử cái này sẽ làm việc cho bạn! Bạn có thể sử dụng điều này với firebase cũng!

Để có được dữ liệu căn cứ hỏa lực! Dùng cái này!

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

Sau đó khi người dùng làm một cái gì đó!

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

Tôi thực sự không muốn phải vượt qua người nghe trong mỗi lần chúng tôi đặt kiểm tra thay đổi, cũng không sử dụng enabled như một cách xác định liệu chúng tôi có nên đặt giá trị hay không (điều gì xảy ra trong trường hợp chúng tôi đã tắt công tắc khi đặt giá trị ?)

Thay vào đó, tôi đang sử dụng các thẻ có id và một vài phương thức mở rộng mà bạn có thể gọi:

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

Bây giờ bạn có thể gọi setCheckedWithoutCallingListener(bool)và nó sẽ thực thi việc sử dụng người nghe chính xác.

Bạn vẫn có thể gọi setChecked(bool)để kích hoạt người nghe nếu bạn vẫn cần nó


0

Tôi đoán sử dụng sự phản chiếu là cách duy nhất. Một cái gì đó như thế này:

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

Có thể hoạt động cho đến khi các nhà phát triển thay đổi tên trường hoặc, ví dụ, kéo lên phương thức "isChecked" trong hệ thống phân cấp ... hoặc thực hiện một phép tái cấu trúc khác ... Ít nhất là thêm một cái gì đó nhưif(Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN){ /* do reflection */}
aeracode 12/215

Đó là một ý tưởng sai lầm khi khuyến khích phá vỡ API và đào sâu vào bên trong. Mọi thay đổi đối với việc triển khai sẽ khiến các ứng dụng bị lỗi.
mspanc

0

Giải pháp của tôi được viết bằng java dựa trên câu trả lời @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 hộp kiểm và luôn luôn sẽ được kiểm tra (một phải được kiểm tra ban đầu). Cài đặt thẻ thành các khối thực sự trên trình ngheCheckedChanged.


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

}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.