Anahtarın, Onay Kutusu Değerinin kullanıcı tarafından mı yoksa programla mı değiştirildiğini (tutma dahil) nasıl ayırt edebilirim?


110
setOnCheckedChangeListener(new OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                // How to check whether the checkbox/switch has been checked
                // by user or it has been checked programatically ?

                if (isNotSetByUser())
                    return;
                handleSetbyUser();
            }
        });

Yöntem nasıl uygulanır isNotSetByUser()?


Emin değilim, ancak kullanıcı bunu değiştirdiyse, o dinleyiciyi ayarlarsanız bir onClick geri araması alırsınız. Yani belki onClick'te bir boole bayrağı ayarlayabilirsin, bu şekilde onCheckChanged'i kullanıcının değişikliği başlatıp başlatmadığını kontrol edebilirsin.
FoamyGuy


Daha basit ve net bir çözümüm var: bkz. Stackoverflow.com/a/41574200/3256989
ultraon

Yanıtlar:


157

Cevap 2:

Çok basit bir cevap:

OnCheckedChangeListener yerine OnClickListener üzerinde kullanın

    someCheckBox.setOnClickListener(new OnClickListener(){

        @Override
        public void onClick(View v) {
            // you might keep a reference to the CheckBox to avoid this class cast
            boolean checked = ((CheckBox)v).isChecked();
            setSomeBoolean(checked);
        }

    });

Artık yalnızca tıklama etkinliklerini alıyorsunuz ve programatik değişiklikler konusunda endişelenmenize gerek yok.


Cevap 1:

Bu sorunu kapsüllenmiş bir şekilde ele alan bir sarmalayıcı sınıfı (bkz. Dekoratör Modeli) oluşturdum:

public class BetterCheckBox extends CheckBox {
    private CompoundButton.OnCheckedChangeListener myListener = null;
    private CheckBox myCheckBox;

    public BetterCheckBox(Context context) {
        super(context);
    }

    public BetterCheckBox(Context context, CheckBox checkBox) {
        this(context);
        this.myCheckBox = checkBox;
    }

    // assorted constructors here...    

    @Override
    public void setOnCheckedChangeListener(
        CompoundButton.OnCheckedChangeListener listener){
        if(listener != null) {
            this.myListener = listener;
        }
        myCheckBox.setOnCheckedChangeListener(listener);
    }

    public void silentlySetChecked(boolean checked){
        toggleListener(false);
        myCheckBox.setChecked(checked);
        toggleListener(true);
    }

    private void toggleListener(boolean on){
        if(on) {
            this.setOnCheckedChangeListener(myListener);
        }
        else {
            this.setOnCheckedChangeListener(null);
        }
    }
}

CheckBox, XML'de hala aynı şekilde ilan edilebilir, ancak bunu GUI'nizi kodda başlatırken kullanın:

BetterCheckBox myCheckBox;

// later...
myCheckBox = new BetterCheckBox(context,
    (CheckBox) view.findViewById(R.id.my_check_box));

Dinleyiciyi tetiklemeden koddan kontrol edilmiş olarak ayarlamak istiyorsanız, myCheckBox.silentlySetChecked(someBoolean)bunun yerine arayın setChecked.


15
A için Switch, cevap 1 hem dokunma hem de kaydırmalı durumlarda işe yararken, cevap 2 yalnızca musluk durumunda çalışacaktır. Kişisel bir tercih, benim sınıf uzatmak yapılmış CheckBox/ Switchyerine tek bir başvuru tutan daha. Bu şekilde daha temiz hissettirir (bunu yaparsanız, XML'de tam paket adını belirtmeniz gerektiğini unutmayın).
howettl

1
Bu antropomo için teşekkürler, neden daha önce düşünmediğimi bilmiyorum, ama bana biraz zaman kazandırdın;). Şerefe!
CyberDandy

Bundan emin değilim, ancak materyal tasarım anahtarını almak için SwitchCompat'ı (appcompat v7 kullanarak) genişletirseniz, yeni yeniden tasarım ve renklendirme yeteneklerini sona erdirebilirsiniz.
DNax

4
Her iki çözümün de ciddi kusurları var. İlk çözüm: Kullanıcı anahtarı sürüklediğinde dinleyici ateşlenmez. İkinci Çözüm: setOnCheckedChangeListener bu null denetimi yaptığından, zaten yeni bir set olduğunda eski dinleyiciyi gerçekten ayarlar.
Paul Woitaschek

İlk çözüm: Kısa bir çözüm istiyorsanız ve bu pencere aracını kullanmak istemiyorsanız bilinen ve yarı kabul edilebilir bir sorun. İkinci çözüm: Bu tür beklenmedik uç durumu ele almak için sıfır kontrolü değiştirildi. Şimdi atamak listeneriçin myListenerher listenerboş değil. Neredeyse tüm durumlarda bu hiçbir şey yapmaz, ancak herhangi bir nedenle yeni bir dinleyici yaparsak bu bozulmaz.
anthropomo

38

Belki isShown () 'ı kontrol edebilirsiniz? DOĞRU ise - kullanıcıdan daha fazla. Benim için çalışıyor.

setOnCheckedChangeListener(new OnCheckedChangeListener() {
    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        if (myCheckBox.isShown()) {// makes sure that this is shown first and user has clicked/dragged it
                  doSometing();
        }
    }
});

1
OnStart () veya onResume () içinde "setChecked (isChecked)" çağırsanız bile çalışır (beklenmeyen geri dönüş olmadığı anlamına gelir). Bu yüzden mükemmel bir çözüm olarak düşünülebilir.
Alan Zhiliang Feng

1
Genel bir çözüm değil gibi görünüyor. Ya düğme o anda gösteriliyorsa ancak değeri koddan değiştirilmişse?
Pavel

1
Anlamıyorum, 'isShown ()' kullanıcı eylemleri ile programatik değişiklikleri nasıl ayırt ediyor? 'isShown ()' doğruysa bunun bir kullanıcı eylemi olduğunu nasıl söyleyebilir?
Muhammed Refaat

1
Bu çözüm yalnızca çok özel durumlarda çalışır ve belgelenmemiş, garantisiz etkinlik oluşturma ve düzenleme sürecine dayanır. Bunun gelecekte bozulmayacağına dair bir garanti yoktur ve bir görünüm zaten oluşturulmuşsa çalışmaz.
Manuel

26

İçinde onCheckedChanged()kullanıcı aslında olup olmadığını sadece çek checked/uncheckedradyo düğmesini ve ardından buna göre şeyler olarak aşağıdaki gibidir:

mMySwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
 @Override
 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
   if (buttonView.isPressed()) {
       // User has clicked check box
    }
   else
    {
       //triggered due to programmatic assignment using 'setChecked()' method.   
    }
  }
});

İyi çözüm, görünümü özelleştirmeye gerek yok.
Aju

Seni seviyorum. : DDD
Vlad

12
kullanıcı kaydırarak / kaydırarak anahtarı değiştirdiğinde bu çalışmıyor
mohitum

1
Bu yaklaşımı her yerde kullanarak, ancak isPressedyanlış döndürdüğü gibi çalışmadığı bir durum bulundu , TalkBack açık Nokia cihazı.
Mikhail

SwitchMaterial sorunsuz çalışır. İyi karar, teşekkürler!
R00We

25

Programatik olarak değiştirmeden önce dinleyiciyi kaldırabilir ve aşağıdaki SO gönderisinde yanıtlandığı gibi tekrar ekleyebilirsiniz:

https://stackoverflow.com/a/14147300/1666070

theCheck.setOnCheckedChangeListener(null);
theCheck.setChecked(false);
theCheck.setOnCheckedChangeListener(toggleButtonChangeListener);

4

CheckBox'ı genişletmeyi deneyin. Bunun gibi bir şey (tam bir örnek değil):

public MyCheckBox extends CheckBox {

   private Boolean isCheckedProgramatically = false;

   public void setChecked(Boolean checked) {
       isCheckedProgramatically = true;
       super.setChecked(checked);
   }

   public Boolean isNotSetByUser() {
      return isCheckedProgramatically;
   }

}

3

Oldukça iyi çalışan başka bir basit çözüm var. Örnek Switch içindir.

public class BetterSwitch extends Switch {
  //Constructors here...

    private boolean mUserTriggered;

    // Use it in listener to check that listener is triggered by the user.
    public boolean isUserTriggered() {
        return mUserTriggered;
    }

    // Override this method to handle the case where user drags the switch
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        boolean result;

        mUserTriggered = true;
        result = super.onTouchEvent(ev);
        mUserTriggered = false;

        return result;
    }

    // Override this method to handle the case where user clicks the switch
    @Override
    public boolean performClick() {
        boolean result;

        mUserTriggered = true;
        result = super.performClick();
        mUserTriggered = false;

        return result;
    }
}

2

İlginç soru. Bildiğim kadarıyla, dinleyiciye girdiğinizde dinleyiciyi hangi eylemin tetiklediğini tespit edemezsiniz, bağlam yeterli değildir. Gösterge olarak harici bir boolean değeri kullanmadığınız sürece.

"Programlı olarak" kutusunu işaretlediğinizde, programlı olarak yapıldığını belirtmek için önceden bir boole değeri ayarlayın. Gibi bir şey:

private boolean boxWasCheckedProgrammatically = false;

....

// Programmatic change:
boxWasCheckedProgrammatically = true;
checkBoxe.setChecked(true)

Ve dinleyicinizde, onay kutusunun durumunu sıfırlamayı unutmayın:

@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    if (isNotSetByUser()) {
        resetBoxCheckSource();
        return;
    }
    doSometing();
}

// in your activity:
public boolean isNotSetByUser() {
    return boxWasCheckedProgrammatically;
}

public void resetBoxCheckedSource() {
    this.boxWasCheckedProgrammatically  = false;
}

2

Deneyin NinjaSwitch:

setChecked(boolean, true)Algılanmadan anahtarın işaretli durumunu değiştirmek için aramanız yeterlidir!

public class NinjaSwitch extends SwitchCompat {

    private OnCheckedChangeListener mCheckedChangeListener;

    public NinjaSwitch(Context context) {
        super(context);
    }

    public NinjaSwitch(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public NinjaSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
        super.setOnCheckedChangeListener(listener);
        mCheckedChangeListener = listener;
    }

    /**
     * <p>Changes the checked state of this button.</p>
     *
     * @param checked true to check the button, false to uncheck it
     * @param isNinja true to change the state like a Ninja, makes no one knows about the change!
     */
    public void setChecked(boolean checked, boolean isNinja) {
        if (isNinja) {
            super.setOnCheckedChangeListener(null);
        }
        setChecked(checked);
        if (isNinja) {
            super.setOnCheckedChangeListener(mCheckedChangeListener);
        }
    }
}

2

Eğer OnClickListenerzaten ayarlanmış ve kullanımı üzerine yazılır olmamalı !buttonView.isPressed()olarak isNotSetByUser().

Aksi takdirde en iyi seçenek OnClickListeneryerine kullanmaktır OnCheckedChangeListener.


buttonView.isPressed () güzel bir çözümdür. OnClickListener'ı kullandığımızda bir sorun var, kullanıcı anahtarda kaydırdığında geri arama almayacağız.
Aju

2

Kabul edilen yanıt, orijinal onay kutusuna bir referansı korumamak için biraz basitleştirilebilir. Bu, onu SilentSwitchCompat(veya SilentCheckboxCompatisterseniz) doğrudan XML içinde kullanabilmemizi sağlar. Belirleyebileceğiniz yüzden de yaptım OnCheckedChangeListeneriçin nullbunu arzu eğer.

public class SilentSwitchCompat extends SwitchCompat {
  private OnCheckedChangeListener listener = null;

  public SilentSwitchCompat(Context context) {
    super(context);
  }

  public SilentSwitchCompat(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  @Override
  public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
    super.setOnCheckedChangeListener(listener);
    this.listener = listener;
  }

  /**
   * Check the {@link SilentSwitchCompat}, without calling the {@code onCheckChangeListener}.
   *
   * @param checked whether this {@link SilentSwitchCompat} should be checked or not.
   */
  public void silentlySetChecked(boolean checked) {
    OnCheckedChangeListener tmpListener = listener;
    setOnCheckedChangeListener(null);
    setChecked(checked);
    setOnCheckedChangeListener(tmpListener);
  }
}

Daha sonra bunu doğrudan XML'inizde şu şekilde kullanabilirsiniz (Not: tüm paket adına ihtiyacınız olacak):

<com.my.package.name.SilentCheckBox
      android:id="@+id/my_check_box"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:textOff="@string/disabled"
      android:textOn="@string/enabled"/>

Ardından aşağıdaki numarayı arayarak kutuyu sessizce işaretleyebilirsiniz:

SilentCheckBox mySilentCheckBox = (SilentCheckBox) findViewById(R.id.my_check_box)
mySilentCheckBox.silentlySetChecked(someBoolean)

1

İşte benim uygulamam

Özel Anahtar için Java Kodu:

public class CustomSwitch extends SwitchCompat {

private OnCheckedChangeListener mListener = null;

public CustomSwitch(Context context) {
    super(context);
}

public CustomSwitch(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public CustomSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

@Override
public void setOnCheckedChangeListener(@Nullable OnCheckedChangeListener listener) {
    if(listener != null && this.mListener != listener) {
        this.mListener = listener;
    }
    super.setOnCheckedChangeListener(listener);
}

public void setCheckedSilently(boolean checked){
    this.setOnCheckedChangeListener(null);
    this.setChecked(checked);
    this.setOnCheckedChangeListener(mListener);
}}

Eşdeğer Kotlin Kodu:

class CustomSwitch : SwitchCompat {

private var mListener: CompoundButton.OnCheckedChangeListener? = null

constructor(context: Context) : super(context) {}

constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {}

constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}

override fun setOnCheckedChangeListener(@Nullable listener: CompoundButton.OnCheckedChangeListener?) {
    if (listener != null && this.mListener != listener) {
        this.mListener = listener
    }
    super.setOnCheckedChangeListener(listener)
}

fun setCheckedSilently(checked: Boolean) {
    this.setOnCheckedChangeListener(null)
    this.isChecked = checked
    this.setOnCheckedChangeListener(mListener)
}}

Dinleyiciyi tetiklemeden anahtar durumunu değiştirmek için şunu kullanın:

swSelection.setCheckedSilently(contact.isSelected)

Durum değişikliğini normalde olduğu gibi şu şekilde izleyebilirsiniz:

swSelection.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
   @Override
   public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
      // Do something
   }       
 });

Kotlin'de:

 swSelection.setOnCheckedChangeListener{buttonView, isChecked -> run {
            contact.isSelected = isChecked
        }}

1

Kotlin uzantı işlevlerine sahip varyantım:

fun CheckBox.setCheckedSilently(isChecked: Boolean, onCheckedChangeListener: CompoundButton.OnCheckedChangeListener) {
    if (isChecked == this.isChecked) return
    this.setOnCheckedChangeListener(null)
    this.isChecked = isChecked
    this.setOnCheckedChangeListener(onCheckedChangeListener)
}

... maalesef her seferinde onCheckedChangeListener'ı geçirmemiz gerekiyor çünkü CheckBox sınıfı mOnCheckedChangeListener alanı ((

Kullanımı:

checkbox.setCheckedSilently(true, myCheckboxListener)

0

Değişken oluştur

boolean setByUser = false;  // Initially it is set programmatically


private void notSetByUser(boolean value) {
   setByUser = value;
}
// If user has changed it will be true, else false 
private boolean isNotSetByUser() {
   return setByUser;          

}

Uygulamada kullanıcı yerine değiştirdiğinizde kullanıcı notSetByUser(true)tarafından ayarlanmaması için arayın, aksi takdirde çağrı notSetByUser(false)yani program tarafından ayarlanır.

Son olarak, olay dinleyicinizde, isNotSetByUser () 'ı çağırdıktan sonra, onu tekrar normale döndürdüğünüzden emin olun.

Bu eylemi kullanıcı aracılığıyla veya programlı olarak gerçekleştirdiğinizde bu yöntemi çağırın. NotSetByUser () yöntemini uygun değerle çağırın.


0

Görünümün etiketi kullanılmıyorsa, onay kutusunu genişletmek yerine onu kullanabilirsiniz:

        checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {

                @Override
                public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
                    if (buttonView.getTag() != null) {
                        buttonView.setTag(null);
                        return;
                    }
                    //handle the checking/unchecking
                    }

onay kutusunu işaretleyen / işareti kaldıran bir şeyi her çağırdığınızda, işaretlemeden / işaretini kaldırmadan önce bunu da çağırın:

checkbox.setTag(true);

0

PublishSubjectBasit bir RxJava's ile uzantı oluşturdum . Yalnızca "OnClick" olaylarına tepki verir.

/**
 * Creates ClickListener and sends switch state on each click
 */
fun CompoundButton.onCheckChangedByUser(): PublishSubject<Boolean> {
    val onCheckChangedByUser: PublishSubject<Boolean> = PublishSubject.create()
    setOnClickListener {
        onCheckChangedByUser.onNext(isChecked)
    }
    return onCheckChangedByUser
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.