Изменить значение флажка, не вызывая onCheckChanged

147

Я setOnCheckedChangeListenerреализовал для моегоcheckbox

Есть ли способ, которым я могу позвонить

checkbox.setChecked(false);

без срабатывания onCheckedChanged

Archie.bpgc
источник
Почему бы просто не использовать простой флаг true / false? Это самый простой способ решить эту проблему, и он занимает всего три строки дополнительного кода. Смотрите мой ответ ниже.
Ручир Барония

Ответы:

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.
тень
Было бы неэффективно менять слушателя, если вы хотите использовать метод setChecked () многократно?
Рен
4
@Ren, изменение слушателя включает только установку свойства в CheckBoxобъекте. Я бы не сказал, что это неэффективно.
тень
Документ гласит: «Вызывается, когда выбранная кнопка-переключатель изменилась. Когда выбор очищен, значение параметра idI равно -1». Это действительно вводит в заблуждение, также должен пройти isChecked.
AA_PV
69

Добавьте этот код внутри OnCheckedChangeListener:

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

Это поможет нам выяснить, было ли состояние checkBox для погоды изменено программно или в результате действий пользователя.

krisDrOid
источник
11
Это должно быть отмечено как решение, работает как шарм!
Эшвин Балани
5
быть в курсе! Это нарушает режим доступности! isPressed () не соответствует действительности, когда он вызывается двойным нажатием в режиме голосового помощника.
A1
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);
разработчик Android
источник
11

вы используете просто setonclickListener, он будет работать нормально, и это очень простой метод, спасибо :)

Рохит
источник
35
onClick () не вызывается, когда пользователь перемещает переключатель.
Джеймс
2
В действительности, если вы перетаскиваете тумблер, то при этом не остается обработчика.
Виктор Г
тогда как вы получите значение isChecked, т.е. истина или ложь?
Абхи
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
    }

Затем при доступе просто установите для тега значение true, прежде чем установить isChecked в значение 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

Krishan
источник
3

Установите null на changeListener перед проверкой переключателя. Вы можете установить слушателя снова после проверки переключателя.

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

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

   }
});
gencaysahinn
источник
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();
vovahost
источник
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
            }

        }
    });
areema
источник
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 также!

Для получения базы данных! Использовать это!

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 приходилось в качестве способа определения, следует ли нам устанавливать значение (что происходит в случае, если у нас уже отключен переключатель при установке значения ?)

Вместо этого я использую теги с идентификатором и несколько методов расширения, которые вы можете вызвать:

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)чтобы уволить слушателя, если он вам все еще нужен

daentech
источник
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();
}
Zielony
источник
Может работать до тех пор, пока разработчики не изменят имя поля или, например, не вызовут метод isChecked в иерархии ... или не выполнят другой рефакторинг ... По крайней мере, добавьте что-то вродеif(Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN){ /* do reflection */}
aeracode
Неправильно предлагать взламывать API и копаться во внутренностях. Любое изменение в реализации приведет к сбою приложений.
Mspanc
0

Мое решение написано в Java на основе ответа @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 флажка и всегда один будет отмечен (хотя один должен быть проверен изначально). Установка тега в истинные блоки на прослушивателе CheckedChanged.

Makalele
источник
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
источник