2012-02-03 13 views
82
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(); 
      } 
     }); 

Come implementare il metodo isNotSetByUser()?In che modo è possibile distinguere se Switch, Valore casella di controllo è stato modificato dall'utente o programmaticamente (incluso dalla conservazione)?

+0

Non sono sicuro, ma penso che se l'utente lo ha attivato, si otterrebbe anche un callback di ClickClick se si imposta quel listener. Quindi forse puoi impostare un flag booleano in onClick in questo modo puoi verificarlo suCheckChanged per vedere se l'utente ha avviato la modifica. – FoamyGuy

+1

correlati [Modifica valore casella di controllo senza attivazione suCheckChanged] (http://stackoverflow.com/q/15523157/1699210), proposto da [krishan] (http://stackoverflow.com/users/2100079/krishan) – bummi

+0

Ho più soluzione semplice e chiara: vedi http://stackoverflow.com/a/41574200/3256989 – ultraon

risposta

130

Risposta 2:

una risposta molto semplice:

all'uso su OnClickListener invece di OnCheckedChangeListener

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

    }); 

Ora è scegliere solo gli eventi click e non devono preoccuparsi di modifiche programmatiche .


Risposta 1:

Ho creato una classe wrapper (vedi Decorator Pattern) che gestisce questo problema in modo incapsulato:

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 può ancora essere dichiarato lo stesso in XML , ma utilizzare questo quando si inizializza la GUI nel codice:

BetterCheckBox myCheckBox; 

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

Se si desidera impostare ch prelevato dal codice senza attivare l'ascoltatore, chiamare myCheckBox.silentlySetChecked(someBoolean) anziché setChecked.

+11

Per uno 'Switch', la risposta 1 funziona sia in casi di tocco che di scorrimento, mentre la risposta 2 funziona solo nel caso di tocco. Come preferenza personale, ho fatto in modo che la mia classe estenda 'CheckBox' /' Switch' piuttosto che mantenere un riferimento a uno. Si sente più pulito in questo modo (nota che devi specificare il nome completo del pacchetto nell'XML se lo fai). – howettl

+1

Grazie per questo anthropomo, non so perché non ci ho pensato prima, ma mi hai salvato un po 'di tempo prezioso;). Saluti ! – CyberDandy

+0

Non ne sono sicuro, ma se estendi SwitchCompat (utilizzando appcompat v7) per ottenere lo switch di progettazione materiale, puoi terminare le nuove funzionalità di riprogettazione e tinting. – DNax

35

Forse è possibile controllare isShown()? Se TRUE - di quanto sia utente. Per me va bene.

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

Grazie a @Denis. Ha funzionato. –

+0

Davvero una bella idea senza creare una classe personalizzata extra .... – Pranav

+1

Funziona (ovvero senza richiamata inaspettata) anche se chiami "setChecked (isChecked)" in onStart() o onResume(). Quindi potrebbe essere considerato una soluzione perfetta. –

1

Interessante domanda. Per quanto ne so, una volta che sei in ascolto, non puoi rilevare quale azione ha attivato l'ascoltatore, il contesto non è sufficiente. A meno che non si utilizzi un valore booleano esterno come indicatore.

Quando si seleziona la casella "programmaticamente", impostare un valore booleano prima per indicare che è stato eseguito a livello di programmazione. Qualcosa di simile:

private boolean boxWasCheckedProgrammatically = false; 

.... 

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

E nel vostro ascoltatore, non dimenticare di ripristinare lo stato della casella di controllo:

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

provare a estendere CheckBox. Qualcosa del genere (ad esempio non è completa):

public MyCheckBox extends CheckBox { 

    private Boolean isCheckedProgramatically = false; 

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

    public Boolean isNotSetByUser() { 
     return isCheckedProgramatically; 
    } 

} 
0

creare una variabile

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;   

} 

Nell'applicazione quando lo si modifica invece dell'utente, chiamare notSetByUser(true) quindi non è impostato dall'utente, altrimenti chiamare notSetByUser(false) cioè è impostato dal programma.

Infine, nel listener dell'evento, dopo aver chiamato isNotSetByUser(), assicurarsi di reimpostarlo di nuovo alla normalità.

Chiamare questo metodo ogni volta che si gestisce quell'azione tramite l'utente o al livello di programmazione. Chiama il notSetByUser() con il valore appropriato.

0

Se tag della vista non viene utilizzato, è possibile utilizzarlo al posto di estendere la casella di controllo:

 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 
        } 

ogni volta che si chiama qualcosa che controlla/deseleziona la casella di controllo, chiamata anche questo prima di controllare/deselezionando:

checkbox.setTag(true); 
2

C'è un'altra soluzione semplice che funziona piuttosto bene. L'esempio è per Switch.

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

È possibile rimuovere l'ascoltatore prima di cambiare programatically e aggiungerlo di nuovo, come risposta nel seguente SO postale:

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

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

bella risposta! ty – zys

1

Prova NinjaSwitch:

basta chiamare setChecked(boolean, true) per cambiare lo stato controllato dello switch senza rilevare!

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

Se OnClickListener è già impostato e non devono essere sovrascritti, utilizzare !buttonView.isPressed() come isNotSetByUser().

In caso contrario, la variante migliore è utilizzare OnClickListener anziché OnCheckedChangeListener.

+0

buttonView.isPressed() è una bella soluzione. C'è un problema quando usiamo OnClickListener, quando l'utente fa scorrere sull'interruttore, non riceveremo la richiamata. – Aju

1

La risposta accettata potrebbe essere semplificata un po 'per non mantenere un riferimento alla casella di controllo originale. Questo lo rende così possiamo usare lo SilentSwitchCompat (o SilentCheckboxCompat se preferisci) direttamente nell'XML. Inoltre, ho deciso di impostare OnCheckedChangeListener su null se lo desideri.

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

È quindi possibile utilizzare questo direttamente in XML in questo modo (Nota: è necessario l'intero nome del pacchetto):

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

Quindi è possibile controllare la casella in silenzio chiamando:

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

All'interno di onCheckedChanged() è sufficiente verificare se l'utente ha effettivamente selezionato/deselezionato il pulsante di opzione e quindi effettuare le seguenti operazioni come segue:

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

});

+0

Buona soluzione, non è necessario personalizzare la vista. – Aju