मैं स्विच, चेकबॉक्स मान को उपयोगकर्ता या प्रोग्रामेटिक रूप से (रिटेंशन सहित) में कैसे बदल सकता हूं?


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

कैसे लागू करें विधि isNotSetByUser()?


मैं निश्चित नहीं हूं, लेकिन मुझे लगता है कि यदि उपयोगकर्ता ने इसे टॉगल किया है तो आपको उस श्रोता को सेट करने पर भी कॉलबैक मिलेगा। तो हो सकता है कि आप सेट कर सकते हैं, लेकिन जिस तरह से आप उपयोगकर्ता द्वारा परिवर्तन शुरू किया है, यह देखने के लिए onCheckChanged में जिस तरह से आप इसे चेक कर सकते हैं, उस पर क्लिक करें।
फामुगी


मेरे पास अधिक सरल और स्पष्ट समाधान है: देखें stackoverflow.com/a/41574200/3256989
अल्ट्रा

जवाबों:


157

उत्तर 2:

एक बहुत ही सरल उत्तर:

OnCheckedChangeListener के बजाय OnClickListener पर उपयोग करें

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

    });

अब आप केवल क्लिक की घटनाओं को उठाते हैं और प्रोग्राम परिवर्तन के बारे में चिंता करने की ज़रूरत नहीं है।


उत्तर 1:

मैंने एक आवरण वर्ग बनाया है (डेकोरेटर पैटर्न देखें) जो इस समस्या को एक संक्षिप्त तरीके से संभालता है:

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

चेकबॉक्स को अभी भी XML में ही घोषित किया जा सकता है, लेकिन कोड में अपने GUI को इनिशियलाइज़ करते समय इसका उपयोग करें:

BetterCheckBox myCheckBox;

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

यदि आप श्रोता को ट्रिगर किए बिना कोड से चेक सेट करना चाहते हैं, तो myCheckBox.silentlySetChecked(someBoolean)इसके बजाय कॉल करें setChecked


15
A के लिए Switch, उत्तर 1 नल और स्लाइड दोनों मामलों में काम करता है, जबकि उत्तर 2 केवल नल के मामले में काम करेगा। एक व्यक्तिगत प्राथमिकता के रूप में, मैंने किसी एक का संदर्भ रखने के बजाय अपनी कक्षा का विस्तार CheckBox/ किया Switch। उस तरह से क्लीनर महसूस करता है (ध्यान दें कि यदि आप ऐसा करते हैं तो आपको XML में पूर्ण पैकेज नाम निर्दिष्ट करना होगा)।
Howettl

1
इस एंथ्रोपोमो के लिए धन्यवाद, मुझे नहीं पता कि मैंने इसके बारे में पहले क्यों नहीं सोचा, लेकिन आपने मुझे कुछ कीमती समय बचाया;)। चियर्स!
साइबरडीन जूल 16'14

मुझे इस बारे में निश्चित नहीं है, लेकिन यदि आप सामग्री डिजाइन स्विच प्राप्त करने के लिए स्विचकॉम (ऐप्पॉमैट वी 7 का उपयोग करके) का विस्तार करते हैं, तो आप नए रीडिज़ाइन और टिनिंग क्षमताओं को समाप्त कर सकते हैं।
डीएनएक्स

4
दोनों समाधानों में गंभीर खामियां हैं। पहला समाधान: जब उपयोगकर्ता स्विच को स्विच करता है तो श्रोता निकाल नहीं पाता है। दूसरा समाधान: क्योंकि setOnCheckedChangeListener करता है कि अशक्त जाँच करता है यह पुराने श्रोता को तब सेट करता है जब कोई नया सेट पहले से हो।
पॉल वोइत्सेक

पहला समाधान: यदि आप संक्षिप्त समाधान चाहते हैं तो ज्ञात और अर्ध-स्वीकार्य मुद्दा और उस विजेट का उपयोग करने का इरादा न करें। दूसरा समाधान: इस तरह के असंभावित किनारे मामले को संबोधित करने के लिए अशक्त जांच को बदल दिया। अब हम जब भी अशक्त नहीं listenerहोते हैं myListenerतब असाइन करते हैं listener। लगभग सभी मामलों में यह कुछ भी नहीं करता है, लेकिन अगर हम किसी कारण से नया श्रोता बनाते हैं, तो यह नहीं टूटेगा।
एन्थ्रोपोमो

38

हो सकता है कि आप जांच कर सकते हैं यदि TRUE - से यह उपयोगकर्ता है। मेरे लिये कार्य करता है।

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
यह काम करता है (जिसका अर्थ है कोई अप्रत्याशित कॉलबैक नहीं) यहां तक ​​कि आप ऑनस्टार्ट () या onResume () में भी "setChecked (isChecked)" कहते हैं। इसलिए इसे एक सही समाधान माना जा सकता है।
एलन झीलियांग फेंग

1
एक सामान्य समाधान नहीं लगता है। क्या होगा यदि बटन क्षण में दिखाया गया है, लेकिन इसका मान कोड से बदल गया है?
पावेल

1
मुझे यह नहीं मिला, कैसे 'शोसन ()' उपयोगकर्ता कार्यों और प्रोग्रामेटिक परिवर्तनों के बीच अंतर करता है? जैसे कि यह कैसे कह सकता है कि यह एक उपयोगकर्ता की कार्रवाई है अगर 'शॉन ()' सच है?
मुहम्मद रिफैट

1
यह समाधान केवल बहुत विशिष्ट मामलों में काम करता है और अनिर्दिष्ट, गैर-गारंटीकृत गतिविधि निर्माण और लेआउटिंग प्रक्रिया पर निर्भर करता है। इसकी कोई गारंटी नहीं है कि यह भविष्य में नहीं टूटेगा और यह काम नहीं करता है यदि कोई दृश्य पहले ही प्रदान किया गया हो।
मैनुअल

26

onCheckedChanged()केवल इस बात की जांच करें कि क्या उपयोगकर्ता के पास वास्तव checked/uncheckedमें रेडियो बटन है और फिर इस प्रकार सामान को निम्न प्रकार से करें:

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

अच्छा समाधान, दृश्य को अनुकूलित करने की आवश्यकता नहीं है।
अजू

मैं तुमसे प्यार करता हूँ। : DDD
व्लाद

12
यह तब काम नहीं कर रहा है जब उपयोगकर्ता स्विच
ऑन

1
हर जगह इस दृष्टिकोण का उपयोग करना, लेकिन एक ऐसा मामला पाया गया जो isPressedरिटर्न के रूप में काम नहीं करता है , टॉकबैक के साथ नोकिया डिवाइस।
मिखाइल

SwitchMaterial समस्या के बिना काम करता है। अच्छा निर्णय, धन्यवाद!
R00We 18

25

आप इसे प्रोग्रामर में बदलने से पहले श्रोता को हटा सकते हैं और इसे फिर से जोड़ सकते हैं, जैसा कि निम्नलिखित SO पोस्ट में दिया गया है:

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

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

4

चेकबॉक्स को विस्तारित करने का प्रयास करें। ऐसा कुछ (पूर्ण उदाहरण नहीं):

public MyCheckBox extends CheckBox {

   private Boolean isCheckedProgramatically = false;

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

   public Boolean isNotSetByUser() {
      return isCheckedProgramatically;
   }

}

3

एक और सरल उपाय है जो बहुत अच्छी तरह से काम करता है। उदाहरण स्विच के लिए है।

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

दिलचस्प सवाल। मेरे ज्ञान के अनुसार, एक बार जब आप श्रोता में होते हैं, तो आप यह नहीं पता लगा सकते हैं कि श्रोता ने क्या कार्रवाई की है, संदर्भ पर्याप्त नहीं है। जब तक आप एक संकेतक के रूप में बाहरी बूलियन मूल्य का उपयोग नहीं करते हैं।

जब आप बॉक्स "प्रोग्रामेटिकली" चेक करते हैं, तो यह इंगित करने के लिए बूलियन मान सेट करें कि यह प्रोग्रामेटिक रूप से किया गया था। कुछ इस तरह:

private boolean boxWasCheckedProgrammatically = false;

....

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

और अपने श्रोता में, चेकबॉक्स की स्थिति को रीसेट करने के लिए मत भूलना:

@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

कोशिश करें NinjaSwitch:

setChecked(boolean, true)बिना पहचाने स्विच का चेक स्टेट बदलने के लिए कॉल करें !

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

यदि OnClickListenerपहले से ही सेट किया गया है और उपयोग ओवरराइट नहीं होना चाहिए, !buttonView.isPressed()के रूप में isNotSetByUser()

अन्यथा सबसे अच्छा संस्करण OnClickListenerइसके बजाय उपयोग करना है OnCheckedChangeListener


buttonView.isPressed () एक अच्छा समाधान है। एक समस्या है जब हम OnClickListener का उपयोग करते हैं, जब उपयोगकर्ता स्विच पर स्लाइड करता है, तो हमें कॉलबैक नहीं मिलेगा।
अजू

2

मूल चेकबॉक्स का संदर्भ नहीं बनाए रखने के लिए स्वीकृत उत्तर को थोड़ा सरल किया जा सकता है। यह ऐसा करता है ताकि हम XML में सीधे SilentSwitchCompat(या SilentCheckboxCompatयदि आप चाहें) का उपयोग कर सकें । तो आप सेट कर सकते हैं मैं भी इसे बनाया OnCheckedChangeListenerकरने के लिए nullयदि आप ऐसा करना चाहते हैं।

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

आप इसे सीधे अपने XML में उपयोग कर सकते हैं जैसे (नोट: आपको पूरे पैकेज नाम की आवश्यकता होगी):

<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"/>

फिर आप कॉल करके बॉक्स को चुपचाप देख सकते हैं:

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

1

यहाँ मेरा कार्यान्वयन है

कस्टम स्विच के लिए जावा कोड:

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

समतुल्य कोटलिन कोड:

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

श्रोता का उपयोग किए बिना ट्रिगर स्थिति बदलने के लिए:

swSelection.setCheckedSilently(contact.isSelected)

आप सामान्य रूप से राज्य परिवर्तन की निगरानी कर सकते हैं:

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

कोटलिन में:

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

1

कोटलिन विस्तार कार्यों के साथ मेरा संस्करण:

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

... दुर्भाग्य से हमें हर बार onCheckedChangeListener को पास करना होगा क्योंकि चेकबॉक्स वर्ग को mOnCheckedChangeListener फ़ील्ड ((

उपयोग:

checkbox.setCheckedSilently(true, myCheckboxListener)

0

एक चर बनाएं

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;          

}

एप्लिकेशन में जब आप उपयोगकर्ता के बजाय इसे बदलते हैं, notSetByUser(true)तो इसे कॉल करें ताकि यह उपयोगकर्ता द्वारा निर्धारित न हो, अन्यथा कॉल करें notSetByUser(false)अर्थात यह प्रोग्राम द्वारा सेट किया गया है।

अंत में, आपके ईवेंट श्रोता में, कॉल करने के बाद, NotSetByUser (), सुनिश्चित करें कि आपने इसे फिर से सामान्य में बदल दिया है।

जब भी आप उस क्रिया को संभाल रहे हों, तो उपयोगकर्ता या प्रोग्रामिक रूप से इस विधि को कॉल करें। उचित मूल्य के साथ notSetByUser () को कॉल करें।


0

यदि दृश्य का टैग उपयोग नहीं किया गया है, तो आप चेकबॉक्स को विस्तारित करने के बजाय इसका उपयोग कर सकते हैं:

        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
                    }

हर बार जब आप चेकबॉक्स को चेक / अनचेक करते हैं, तो चेक / अनचेक करने से पहले इसे कॉल करें:

checkbox.setTag(true);

0

मैंने RxJava के साथ विस्तार किया है PublishSubject, सरल। केवल "OnClick" घटनाओं पर प्रतिक्रिया करता है।

/**
 * 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
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.