Android Spinner: obtenez l'événement de changement d'élément sélectionné

407

Comment pouvez-vous définir l'écouteur d'événements pour un Spinner lorsque l'élément sélectionné change?

Fondamentalement, ce que j'essaie de faire est quelque chose de similaire à ceci:

spinner1.onSelectionChange = handleSelectionChange;

void handleSelectionChange(Object sender){
    //handle event
}
Soni Ali
la source
J'ai essayé ces réponses, mais personne n'a été utile. Une fois que le composant Spinner ne prend pas en charge les événements de clic d'objet. Documentation de Spinner

Réponses:

812

Certaines des réponses précédentes ne sont pas correctes. Ils fonctionnent pour d'autres widgets et vues, mais la documentation du widget Spinner indique clairement:

Un spinner ne prend pas en charge les événements de clic sur un élément. L'appel de cette méthode déclenche une exception.

Mieux vaut utiliser OnItemSelectedListener () à la place:

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
    }

});

Cela fonctionne pour moi.

Notez que la méthode onItemSelected est également invoquée lors de la génération de la vue, vous pouvez donc envisager de la placer dans l' onCreate()appel de méthode.

znq
la source
47
le problème est que la méthode onItemSelected est également invoquée lors de la génération de la vue. Ainsi, le code qui y est écrit est également exécuté au démarrage. Existe-t-il un moyen d'exécuter le code conteneur uniquement s'il existe une véritable sélection d'élément invoquée par l'utilisateur?
Kennethvr
39
en fait, ce problème peut être résolu en plaçant setOnItemSelectedListener dans la méthode OnStart de remplacement et non dans la méthode onCreate. stupide moi ...
Kennethvr
16
J'ai mis l'écouteur dans la méthode onStart, mais il est appelé avant que l'utilisateur ne voie quoi que ce soit, tout comme onCreate, donc, dans mon cas où un bouton "continuer" qui est censé être invisible jusqu'à ce que l'utilisateur sélectionne quelque chose, le bouton est rendu visible lors de l'affichage initial. Êtes-vous en train de dire que votre expérience est différente? Si oui, que faites-vous différemment dans la méthode onStart qui me manque?
Yevgeny Simkin
7
Utilisez un autre champ à l'intérieur de votre écouteur anonyme pour enregistrer la première sélection et dites à onItemSelected de ne rien faire à moins qu'une sélection n'ait été rencontrée? Juste une pensée.
Sam Svenbjorgchristiensensen
4
Mais que se passe-t-il si l'utilisateur sélectionne l'élément "par défaut", celui en haut? Alors onItemSelected(...)n'est pas touché. (Je sais parce que je viens de découvrir cela à la dure.)
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;
    } 
}); 

Remarque: N'oubliez pas une chose.

L' OnItemSelectedListenerévénement Spinner s'exécutera deux fois:

  1. Initialisation de spinner
  2. Utilisateur sélectionné manuellement

Essayez de différencier ces deux en utilisant la variable indicateur.

Santhosh
la source
3
Le mien est appelé deux fois aussi! Je ne peux pas comprendre comment vous différenciez les deux?
MAC
18
Définissez simplement un booléen global comme Boolean initialDisplay = true; Et puis, dans votre onSelect, voyez si c'est vrai et si c'est le cas, ne faites rien d'autre que vous alliez faire sur select mais définissez le drapeau sur false, pour la prochaine fois qu'il est appelé (lorsque l'utilisateur sélectionne réellement).
Yevgeny Simkin,
Meilleure explication sur l'exécution d'OnclickListener.
Pankaj Kumar
6
Je suis personnellement consterné par le fait que quelque chose d'aussi simple - un widget d'interface utilisateur aussi fondamental - soit tellement difficile à mettre en œuvre ... sérieusement - à quel point serait-il difficile de créer une propriété "élément d'affichage par défaut" et de construire la propriété de drapeau booléen dans l'objet classe elle-même ?? Je ne suis pas fan d'Objective C mais je dirai que la mise en œuvre d'un widget iOS prend environ 1 / 10e du temps sur Android.
Bennett Von Bennett
4
Je suis également d'accord. Le Spinner est un widget foutu. Il est très difficile de savoir quand le popup ou le drop est ouvert ou fermé. Aurait-il été si difficile d'ajouter un événement pour cela? La solution ci-dessus peut PRESQUE vous dire quand la liste est ouverte ou fermée mais a trois problèmes: (1) il n'y a pas d'événement pour sélectionner l'élément déjà sélectionné (et la liste se ferme) et (2) il n'y a pas d'événement pour abandonner (toucher hors liste pour le fermer) et (3) onNothingSelected ne semble jamais se déclencher pour moi.
Batdude
19

Vous pouvez implémenter la AdapterView.OnItemSelectedListenerclasse dans votre activité.

Et puis utilisez la ligne ci-dessous dans onCreate()

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

Remplacez ensuite ces deux méthodes:

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

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

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

Vous pouvez éviter l'appel de OnItemSelectedListener () avec une simple vérification: stockez l'index de sélection actuel dans une variable entière et vérifiez dans onItemSelected (..) avant de faire quoi que ce soit.

Par exemple:

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

La cause du iCurrentSelectiondevrait être portée d'objet pour que cela fonctionne!

Sampath
la source
1
Vous ne pouvez pas utiliser une variable non finale dans une classe interne anonyme. Si la iCurrentSelectionvariable est déclarée dans cette classe anonyme, cela fonctionnera correctement. Vous pouvez l'initialiser à -1 pour que le code soit exécuté lors du premier appel.
dahvyd
2
@dahvyd était correct si vous utilisez ceci, l'int doit être final. En tout cas ça marche vraiment bien. Je désactivais un champ EditText si la position 0 n'était pas sélectionnée et si elle changeait, réactivez-la. Merci pour cela.
natur3
8

Trouvez votre nom de spinner et trouvez id puis implémentez cette méthode.

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
    }
});
droidster.me
la source
8

Peu importe si vous définissez OnItemSelectedListener dans onCreate ou onStart - il sera toujours appelé lors de la création ou du démarrage de l'activité (respectivement).
Nous pouvons donc le définir dans onCreate (et PAS dans onStart!).
Ajoutez simplement un drapeau pour comprendre la première initialisation:

private Spinner mSpinner;
private boolean mSpinnerInitialized;

puis dans onCreate (ou onCreateView) juste:

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;
            }
        });
Leo Droidcoder
la source
1
Merci d'utiliser ce drapeau.
Javan R.
7

Les documents pour le widget spinner indiquent

Un spinner ne prend pas en charge les événements de clic sur un élément.

Vous devez utiliser setOnItemSelectedListenerpour gérer votre problème.

johndotnet
la source
6
spinner1.setOnItemSelectedListener(
    new AdapterView.OnItemSelectedListener() {
        //add some code here
    }
);
Chiwai Chan
la source
1
Cela ne résout pas le problème de l'appel de ce rappel lorsque le spinner est lancé pour la première fois (provoquant ainsi une réponse qui n'a rien à voir avec un élément réellement sélectionné).
Yevgeny Simkin
4

prendre une variable globale pour la sélection actuelle de 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>
indrajeet
la source
4

Si vous voulez un vrai onChangedListener (). Stockez la valeur initiale dans le gestionnaire et vérifiez si elle a changé. Il est simple et ne nécessite pas de variable globale. Fonctionne si vous avez plus d'un spinner sur la page.

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

Les objets sont votre ami, utilisez-les.

fishjd
la source
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
            }
        );
kaub0st3r
la source
1

Cela fonctionnera pour initialiser le spinner et findviewbyid et l'utiliser cela fonctionnera

    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
        }

    });
sanjay
la source
1

La meilleure façon ce que je pense serait d'avoir un flagitemselected = 0;dans onCreate(). Et sur l'élément sélectionné, incrémentez l'événement, c'est-à-dire flagitemselected++; puis vérifiez

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

Cela aidera, je suppose.

Pinakin
la source
0

Une astuce que j'ai trouvée consistait à placer vos setOnItemSelectedListeners dans onWindowFocusChanged au lieu de onCreate. Je n'ai pas encore trouvé de mauvais effets secondaires à le faire de cette façon. Fondamentalement, configurez les écouteurs une fois la fenêtre dessinée. Je ne sais pas combien de fois onWindowFocusChanged s'exécute, mais il est assez facile de créer vous-même une variable de verrouillage si vous la trouvez trop souvent.

Je pense qu'Android peut utiliser un système de traitement basé sur les messages, et si vous mettez tout cela dans onCreate, vous pouvez rencontrer des situations où le spinner est rempli après avoir été dessiné. Ainsi, votre auditeur se déclenchera après avoir défini l'emplacement de l'élément. C'est une supposition éclairée, bien sûr, mais n'hésitez pas à me corriger à ce sujet.

Joe Plante
la source
0

Par défaut, vous obtiendrez le premier élément du tableau de spinner via

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

chaque fois que vous avez sélectionné la valeur dans le spinner, cela vous donnera la valeur sélectionnée

si vous voulez la position de l'élément sélectionné, faites-le comme ça

pos = spinner.getSelectedItemPosition();

les deux réponses ci-dessus sont pour sans appliquer l'auditeur

Adam Noor
la source