Android Spinner: ottieni l'evento di cambio oggetto selezionato


407

Come puoi impostare il listener di eventi per uno Spinner quando cambia l'elemento selezionato?

Fondamentalmente quello che sto cercando di fare è qualcosa di simile a questo:

spinner1.onSelectionChange = handleSelectionChange;

void handleSelectionChange(Object sender){
    //handle event
}

Ho provato queste risposte, ma nessuno è stato utile. Una volta che il componente Spinner non supporta gli eventi clic oggetto. Documentazione Spinner

Risposte:


812

Alcune delle risposte precedenti non sono corrette. Funzionano con altri widget e viste, ma la documentazione per il widget Spinner indica chiaramente:

Uno spinner non supporta eventi di clic su elementi. La chiamata a questo metodo genererà un'eccezione.

Meglio usare OnItemSelectedListener () invece:

spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
        // your code here
    }

    @Override
    public void onNothingSelected(AdapterView<?> parentView) {
        // your code here
    }

});

Questo funziona per me.

Si noti che il metodo onItemSelected viene anche richiamato durante la creazione della vista, quindi è possibile considerare di inserirlo nella onCreate()chiamata del metodo.


47
il problema è che anche il metodo onItemSelected viene richiamato durante la creazione della vista. Quindi anche il codice scritto lì viene eseguito all'avvio. Esiste un modo per eseguire il codice contenitore solo se esiste una selezione di elementi reali invocata dall'utente?
Kennethvr,

39
in realtà quel problema può essere risolto inserendo setOnItemSelectedListener nel metodo OnStart di override e non nel metodo onCreate. stupido me ...
Kennethvr,

16
Ho inserito l'ascoltatore nel metodo onStart, ma viene chiamato prima che l'utente riesca a vedere qualcosa, proprio come onCreate è, quindi, nel mio caso in cui un pulsante "procedi" che deve essere invisibile fino a quando l'utente seleziona qualcosa, il pulsante è reso visibile al display iniziale. Stai dicendo che la tua esperienza è diversa? In tal caso, cosa stai facendo diversamente nel metodo onStart che mi manca?
Evgenij Simkin,

7
Utilizzare un altro campo all'interno del listener anonimo per registrare la prima selezione e dire a OnItemSelected di non fare nulla a meno che non sia stata rilevata una selezione? Solo un pensiero.
Sam Svenbjorgchristiensensen,

4
Ma cosa succede se l'utente seleziona l'elemento "predefinito", quello in alto? Quindi onItemSelected(...)non viene colpito. (Lo so perché l'ho appena scoperto nel modo più difficile.)
Andrew Wyld,

55
Spinner spnLocale = (Spinner)findViewById(R.id.spnLocale);

spnLocale.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) { 
        // Your code here
    } 

    public void onNothingSelected(AdapterView<?> adapterView) {
        return;
    } 
}); 

Nota: ricorda una cosa.

L' OnItemSelectedListenerevento Spinner verrà eseguito due volte:

  1. Inizializzazione Spinner
  2. Utente selezionato manualmente

Prova a differenziare quei due usando la variabile flag.


3
Anche il mio viene chiamato due volte! Non riesco a capire come si fa a distinguere tra i due?
MAC,

18
Basta impostare un valore booleano globale come Boolean initialDisplay = true; E poi nella tua onSelect vedi se è vero e se lo è, non fare qualsiasi altra cosa avresti fatto sulla selezione ma imposta il flag su false, per la prossima volta che viene chiamato (quando l'utente seleziona effettivamente).
Evgenij Simkin,

Migliore spiegazione sull'esecuzione di OnclickListener.
Pankaj Kumar,

6
Sono personalmente inorridito dal fatto che qualcosa di così semplice - un widget dell'interfaccia utente così fondamentale - sia così dannatamente difficile da implementare ... sul serio - quanto sarebbe difficile costruire una proprietà 'default show item' e costruire la proprietà flag booleana nell'oggetto classe stessa ?? Non sono un fan di Objective C, ma dirò che l'implementazione del widget iOS richiede circa 1/10 del tempo che fa in Android.
Bennett Von Bennett,

4
Sono anche d'accordo. Lo Spinner è un widget rovinato. È molto difficile sapere quando il popup o il drop sono aperti o chiusi. Sarebbe stato così difficile aggiungere un evento per questo? La soluzione sopra può SEMPRE dirti quando l'elenco è aperto o chiuso ma ha tre problemi: (1) non c'è nessun evento per selezionare l'elemento già selezionato (e l'elenco si chiude) e (2) non c'è nessun evento da interrompere (toccare off-list per chiuderlo) e (3) onNothingSelected sembra non sparare mai per me.
Batdude,

19

Puoi implementare AdapterView.OnItemSelectedListenerclasse nella tua attività.

E poi usa la riga sotto all'interno onCreate()

Spinner spin = (Spinner) findViewById(R.id.spinner);
spin.setOnItemSelectedListener(this);

Quindi sovrascrivere questi due metodi:

public void onItemSelected(AdapterView<?> parent, View v, int position, long id) {
    selection.setText(items[position]);
}

public void onNothingSelected(AdapterView<?> parent) {
    selection.setText("");
}

16

https://stackoverflow.com/q/1714426/811625

Puoi evitare che OnItemSelectedListener () venga chiamato con un semplice controllo: archivia l'indice di selezione corrente in una variabile intera e controlla all'interno di onItemSelected (..) prima di eseguire qualsiasi operazione.

Per esempio:

Spinner spnLocale;

spnLocale = (Spinner)findViewById(R.id.spnLocale);

int iCurrentSelection = spnLocale.getSelectedItemPosition();

spnLocale.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) { 
    if (iCurrentSelection != i){
            // Your code here
    }
    iCurrentSelection = i;
    } 

    public void onNothingSelected(AdapterView<?> adapterView) {
        return;
    } 
}); 

Di causa iCurrentSelectiondovrebbe essere nell'ambito dell'oggetto affinché questo funzioni!


1
Non è possibile utilizzare una variabile non finale all'interno di una classe interna anonima. Se la iCurrentSelectionvariabile viene dichiarata all'interno di questa classe anonima funzionerà correttamente. È possibile inizializzarlo su -1 in modo che il codice venga eseguito alla prima chiamata.
Dahvyd,

2
@dahvyd era corretto se lo usi l'int deve essere definitivo. In ogni caso funziona davvero bene. Stavo disabilitando un campo EditText se la posizione 0 non era selezionata e se cambiava riattivarlo. Grazie per questo.
natur3

8

Trova il nome del tuo spinner e trova l'id quindi implementa questo metodo.

spinnername.setOnItemSelectedListener(new OnItemSelectedListener() {

    @Override
    public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
        // your code here
    }

    @Override
    public void onNothingSelected(AdapterView<?> parentView) {
        // your code here
    }
});

8

Non importa imposterai OnItemSelectedListener in onCreate o onStart - verrà comunque chiamato durante la creazione o l'avvio dell'attività (rispettivamente).
Quindi possiamo impostarlo in onCreate (e NON in onStart!).
Basta aggiungere un flag per capire la prima inizializzazione:

private Spinner mSpinner;
private boolean mSpinnerInitialized;

quindi in onCreate (o onCreateView) solo:

mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                if (!mSpinnerInitialized) {
                    mSpinnerInitialized = true;
                    return;
                }

                // do stuff
            }

            public void onNothingSelected(AdapterView<?> adapterView) {
                return;
            }
        });

1
Grazie per aver usato questa bandiera.
Javan R.,

7

La documentazione per il widget di spinner dice

Uno spinner non supporta eventi di clic su elementi.

Dovresti usare setOnItemSelectedListenerper gestire il tuo problema.


6
spinner1.setOnItemSelectedListener(
    new AdapterView.OnItemSelectedListener() {
        //add some code here
    }
);

1
Questo non risolve il problema di questo callback chiamato quando viene avviato per la prima volta lo spinner (provocando così una risposta che non ha nulla a che fare con un oggetto effettivamente selezionato).
Evgenij Simkin,

4

prendere una variabile globale per l'attuale selezione di spinner:

int currentItem = 0;

spinner_counter = (Spinner)findViewById(R.id.spinner_counter);
String[] value={"20","40","60","80","100","All"};
aa=new ArrayAdapter<String>(this,R.layout.spinner_item_profile,value);
aa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner_counter.setAdapter(aa);

spinner_counter.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            if(currentItem == position){
                return; //do nothing
            }
            else
            {
                 TextView spinner_item_text = (TextView) view;
                 //write your code here
            }
            currentItem = position;
        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {

        }
    });

//R.layout.spinner_item_profile
<?xml version="1.0" encoding="utf-8"?>

<TextView  android:id="@+id/spinner_item_text"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" 
android:layout_height="wrap_content"
android:background="@drawable/border_close_profile"
android:gravity="start"  
android:textColor="@color/black"         
android:paddingLeft="5dip"
android:paddingStart="5dip"
android:paddingTop="12dip"
android:paddingBottom="12dip"
/>

//drawable/border_close_profile
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
  <item>
   <shape android:shape="rectangle">
    <solid android:color="#e2e3d7" />
   </shape>
 </item>
<item android:left="1dp"
android:right="1dp"
android:top="1dp"
android:bottom="1dp">
<shape android:shape="rectangle">
    <solid android:color="@color/white_text" />
</shape>
</item>
</layer-list>

4

Se vuoi un vero onChangedListener (). Memorizzare il valore iniziale nel gestore e verificare se è stato modificato. È semplice e non richiede una variabile globale. Funziona se hai più di un filatore sulla pagina.

String initialValue = // get from Database or your object
mySpinner.setOnItemSelectedListener(new SpinnerSelectedListener(initialValue));

...

protected class SpinnerSelectedListener implements AdapterView.OnItemSelectedListener {

        private SpinnerSelectedListener() {
            super();
        }

        public SpinnerSelectedListener(String initialValue) {
            this();
            this.initialValue = initialValue;
        }

        private String initialValue;

        // getter and setter removed.  

        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            final String newValue = (String) spinHeight.getItemAtPosition(position);
            if (newValue.equals(initialValue) == false) {
               // Add your code here.  The spinner has changed value. 

               // Maybe useful.   
               // initialValue = newValue;
            }

        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {
               // Maybe useful.   
               // initialValue = null; 
        }
    }

Gli oggetti sono tuoi amici, usali.


3
spinner.setOnItemSelectedListener(
            new AdapterView.OnItemSelectedListener() {

                @Override
                public void onItemSelected(AdapterView<?> arg0, View arg1,
                        int arg2, long arg3) {

                    // TODO Auto-generated method stub
                }

                @Override
                public void onNothingSelected(AdapterView<?> arg0) {
                    // TODO Auto-generated method stub

                }
                //add some code here
            }
        );

1

Questo funzionerà per inizializzare lo spinner e findviewbyid e usarlo funzionerà

    Spinner schemeStatusSpinner;

  schemeStatusSpinner = (Spinner) dialog.findViewById(R.id.spinner);

schemeStatusSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
            // your code here
            if(schemeStatusSpinner.getSelectedItemId()==4){
                reasonll.setVisibility(View.VISIBLE);
            }
            else {
                reasonll.setVisibility(View.GONE);
            }
        }

        @Override
        public void onNothingSelected(AdapterView<?> parentView) {
            // your code here
        }

    });

1

Il modo migliore in cui penso sarebbe avere un flagitemselected = 0;in onCreate(). E sull'elemento selezionato l'evento incrementa quel flag cioè flagitemselected++; e poi controlla

if(flagitemselected!=1)
{
// do your work here
}

Questo aiuterà, suppongo.


0

Un trucco che ho trovato è stato quello di mettere il tuo setOnItemSelectedListeners in onWindowFocusChanged anziché inCreate. Non ho ancora riscontrato alcun effetto collaterale negativo in questo modo. Fondamentalmente, imposta gli ascoltatori dopo che la finestra viene disegnata. Non sono sicuro di quanto spesso viene eseguito onWindowFocusChanged, ma è abbastanza facile crearti una variabile di blocco se la trovi in ​​esecuzione troppo spesso.

Penso che Android potrebbe utilizzare un sistema di elaborazione basato sui messaggi e, se lo metti tutto in OnCreate, potresti imbatterti in situazioni in cui lo spinner viene popolato dopo essere stato disegnato. Quindi, il tuo ascoltatore si spegnerà dopo aver impostato la posizione dell'oggetto. Questa è un'ipotesi colta, ovviamente, ma sentiti libero di correggermi su questo.


0

Per impostazione predefinita, otterrai il primo elemento dell'array spinner

value = spinner.getSelectedItem().toString();

ogni volta che hai selezionato il valore nello spinner, questo ti darà il valore selezionato

se vuoi la posizione dell'elemento selezionato, fallo in quel modo

pos = spinner.getSelectedItemPosition();

le due risposte precedenti sono valide senza applicare listener

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.