J'ai pensé à des moyens moins qu'élégants de résoudre ce problème, mais je sais que je dois manquer quelque chose.
Mon onItemSelected
se déclenche immédiatement sans aucune interaction avec l'utilisateur, et c'est un comportement indésirable. Je souhaite que l'interface utilisateur attende que l'utilisateur sélectionne quelque chose avant de faire quoi que ce soit.
J'ai même essayé de configurer l'auditeur dans le onResume()
, en espérant que cela aiderait, mais ce n'est pas le cas.
Comment puis-je empêcher cela de se déclencher avant que l'utilisateur puisse toucher la commande?
public class CMSHome extends Activity {
private Spinner spinner;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Heres my spinner ///////////////////////////////////////////
spinner = (Spinner) findViewById(R.id.spinner);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
this, R.array.pm_list, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
};
public void onResume() {
super.onResume();
spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());
}
public class MyOnItemSelectedListener implements OnItemSelectedListener {
public void onItemSelected(AdapterView<?> parent,
View view, int pos, long id) {
Intent i = new Intent(CMSHome.this, ListProjects.class);
i.putExtra("bEmpID", parent.getItemAtPosition(pos).toString());
startActivity(i);
Toast.makeText(parent.getContext(), "The pm is " +
parent.getItemAtPosition(pos).toString(), Toast.LENGTH_LONG).show();
}
public void onNothingSelected(AdapterView parent) {
// Do nothing.
}
}
}
android
spinner
android-spinner
FauxReal
la source
la source
Spinner
vide et à l'intérieur,onItemSelected
vous pouvez détecter si la chaîne n'est pas vide alorsstartActivity
!Réponses:
Je m'attendais à ce que votre solution fonctionne - je pensais que l'événement de sélection ne se déclencherait pas si vous définissiez l'adaptateur avant de configurer l'écouteur.
Cela étant dit, un simple drapeau booléen vous permettrait de détecter le premier événement de sélection non autorisé et de l'ignorer.
la source
onResume()
etonPostResume()
, donc tous les crochets normaux sont terminés au moment où la disposition se produit.L'utilisation de Runnables est complètement incorrecte.
Utiliser
setSelection(position, false);
dans la sélection initiale avantsetOnItemSelectedListener(listener)
De cette façon, vous définissez votre sélection sans animation, ce qui provoque l'appel de l'écouteur sélectionné sur l'élément. Mais l'écouteur est nul, donc rien n'est exécuté. Ensuite, votre auditeur est affecté.
Suivez donc cette séquence exacte:
la source
En vous référant à la réponse de Dan Dyer, essayez d'enregistrer le
OnSelectListener
dans unepost(Runnable)
méthode:En faisant cela pour moi, le comportement souhaité s'est finalement produit.
Dans ce cas, cela signifie également que l'auditeur ne se déclenche que sur un élément modifié.
la source
onCreate()
,onResume()
etc. Dans ce cas, c'est une astuce fantastique, sans danger de condition de concurrence. J'utilise normalement cette astuceonCreate()
juste après le code de mise en page.J'ai créé une petite méthode utilitaire pour changer de
Spinner
sélection sans avertir l'utilisateur:Il désactive l'écouteur, modifie la sélection et réactive l'écouteur par la suite.
L'astuce est que les appels sont asynchrones au thread d'interface utilisateur, vous devez donc le faire dans des publications de gestionnaire consécutives.
la source
setSpinnerSelectionWithoutCallingListener
deux fois rapidement, de sorte que le deuxième appel soit effectué alors que le premier a déjà défini l'auditeur surnull
, votre spinner sera bloqué pournull
toujours avec un écouteur. Je propose le correctif suivant: ajouterif (listener == null) return;
aprèsspinner.setSelection(selection)
.Malheureusement, il semble que les deux solutions les plus couramment suggérées à ce problème, à savoir le comptage des occurrences de rappel et la publication d'un Runnable pour définir le rappel ultérieurement, peuvent échouer lorsque, par exemple, les options d'accessibilité sont activées. Voici une classe d'assistance qui contourne ces problèmes. Une explication supplémentaire se trouve dans le bloc de commentaires.
la source
J'ai eu BEAUCOUP de problèmes avec le tir de spinner quand je ne le voulais pas, et toutes les réponses ici ne sont pas fiables. Ils fonctionnent - mais seulement parfois. Vous finirez par rencontrer des scénarios où ils échoueront et introduire des bogues dans votre code.
Ce qui a fonctionné pour moi, c'était de stocker le dernier index sélectionné dans une variable et de l'évaluer dans l'écouteur. Si c'est la même chose que le nouvel index sélectionné, ne rien faire et retourner, sinon continuer avec l'écouteur. Faites ceci:
Faites-moi confiance quand je dis cela, c'est de loin la solution la plus fiable. Un hack, mais ça marche!
la source
J'étais dans une situation similaire et j'ai une solution simple qui fonctionne pour moi.
Cela ressemble à des méthodes
setSelection(int position)
et àsetSelected(int position, boolean animate)
une implémentation interne différente.Lorsque vous utilisez la deuxième méthode
setSelected(int position, boolean animate)
avec un faux drapeau d'animation, vous obtenez la sélection sans déclencher l'onItemSelected
auditeur.la source
setSelection(int position, boolean animate);
onItemSelected
dans API23Juste pour étoffer les conseils d'utilisation de onTouchListener pour distinguer les appels automatiques à setOnItemSelectedListener (qui font partie de l'initialisation de l'activité, etc.) des appels déclenchés par une interaction réelle de l'utilisateur, j'ai fait ce qui suit après avoir essayé d'autres suggestions ici et a constaté qu'il fonctionnait bien avec le moins de lignes de code.
Définissez simplement un champ booléen pour votre activité / fragment comme:
Ensuite, juste avant de définir setOnItemSelectedListener de votre spinner, définissez un onTouchListener:
la source
la source
Après avoir tiré mes cheveux pendant longtemps, j'ai créé ma propre classe Spinner. Je lui ai ajouté une méthode qui déconnecte et connecte l'auditeur de manière appropriée.
Utilisez-le dans votre XML comme ceci:
Tout ce que vous avez à faire est de récupérer l'instance de SaneSpinner après l'inflation et la sélection de l'appel comme ceci:
Avec cela, aucun événement n'est déclenché et l'interaction de l'utilisateur n'est pas interrompue. Cela a beaucoup réduit la complexité de mon code. Cela devrait être inclus dans le stock Android car c'est vraiment un PITA.
la source
Aucun événement indésirable de la phase de mise en page si vous retardez l'ajout de l'écouteur jusqu'à la fin de la mise en page:
la source
ViewTreeObserver.OnGlobalLayoutListener
versions sous J en appelantViewTreeObserver.removeGlobalOnLayoutListener
, ce qui est obsolète et porte un nom similaire à la méthode utilisée par cette réponse.Cela se produira si vous effectuez une sélection dans le code en tant que;
Au lieu de l'instruction ci-dessus, utilisez
Modifier: cette méthode ne fonctionne pas pour Mi Android Version Mi UI.
la source
J'ai obtenu une réponse très simple, sûre à 100%:
la source
J'ai trouvé une solution beaucoup plus élégante à cela. Il s'agit de compter le nombre de fois où ArrayAdapter (dans votre cas, "adaptateur") a été appelé. Disons que vous avez 1 spinner et que vous appelez:
Déclarez un compteur int après la méthode onCreate puis à l'intérieur de la méthode onItemSelected () mettez une condition "if" pour vérifier combien de fois l'atapter a été appelé. Dans votre cas, vous ne l’avez appelé qu’une seule fois:
la source
Ma petite contribution est une variation de certains des éléments ci-dessus qui m'a plu à quelques reprises.
Déclarez une variable entière comme valeur par défaut (ou dernière valeur utilisée enregistrée dans les préférences). Utilisez spinner.setSelection (myDefault) pour définir cette valeur avant que l'écouteur ne soit enregistré. Dans onItemSelected, vérifiez si la nouvelle valeur de spinner est égale à la valeur que vous avez affectée avant d'exécuter un autre code.
Cela présente l'avantage supplémentaire de ne pas exécuter de code si l'utilisateur sélectionne à nouveau la même valeur.
la source
Après avoir eu le même problème, je suis arrivé à ces solutions en utilisant des balises. L'idée derrière elle est simple: chaque fois que le spinner est changé par programme, assurez-vous que la balise reflète la position sélectionnée. Dans l'auditeur, vous vérifiez si la position sélectionnée est égale à la balise. Si c'est le cas, la sélection de spinner a été modifiée par programme.
Ci-dessous, ma nouvelle classe "spinner proxy":
Vous aurez également besoin d'un fichier XML avec la configuration de balise dans votre
Values
répertoire. J'ai nommé mon dossierspinner_tag.xml
, mais ça dépend de vous. Cela ressemble à ceci:Remplacez maintenant
dans votre code avec
Et faites ressembler votre gestionnaire à ceci:
La fonction
isUiTriggered()
renverra true si et seulement si le spinner a été changé par l'utilisateur. Notez que cette fonction a un effet secondaire - elle définira la balise, donc un deuxième appel dans le même appel d'écoute reviendra toujoursfalse
.Ce wrapper gérera également le problème avec l'écouteur appelé lors de la création de la disposition.
Amusez-vous, Jens.
la source
Étant donné que rien n'a fonctionné pour moi et que j'ai plus d'un spinner à mon avis (et à mon humble avis, tenir une carte booléenne est une exagération), j'utilise la balise pour compter les clics:
la source
Beaucoup de réponses déjà, voici la mienne.
J'étends
AppCompatSpinner
et j'ajoute une méthodepgmSetSelection(int pos)
qui permet le réglage de sélection programmatique sans déclencher un rappel de sélection. J'ai codé cela avec RxJava afin que les événements de sélection soient livrés via unObservable
.Un exemple de son utilisation, appelé
onCreateView()
dans unFragment
exemple:où
setSelection()
est une méthode dans la vue englobante qui ressemble à ceci, et qui est appelée à la fois à partir des événements de sélection des utilisateurs via leObservable
et également ailleurs par programmation, de sorte que la logique de gestion des sélections est commune aux deux méthodes de sélection.la source
J'essaierais d'appeler
après avoir appelé setAdapter (). Essayez également d'appeler avant l'adaptateur.
Vous avez toujours la solution pour aller avec le sous-classement, où vous pouvez encapsuler un indicateur booléen dans votre méthode setAdapter substituée pour ignorer l'événement.
la source
La solution avec un drapeau booléen ou un compteur ne m'a pas aidé, car lors d'un changement d'orientation onItemSelected () appelle "survoler" le drapeau ou le compteur.
J'ai sous
android.widget.Spinner
- classé et fait de petits ajouts. Les parties pertinentes sont ci-dessous. Cette solution a fonctionné pour moi.la source
Ce n'est pas non plus une solution élégante. En fait c'est plutôt Rube-Goldberg mais ça semble marcher. Je m'assure que le spinner a été utilisé au moins une fois en étendant l'adaptateur de tableau et en remplaçant son getDropDownView. Dans la nouvelle méthode getDropDownView, j'ai un indicateur booléen qui est configuré pour montrer que le menu déroulant a été utilisé au moins une fois. J'ignore les appels à l'auditeur jusqu'à ce que l'indicateur soit défini.
MainActivity.onCreate ():
adaptateur de matrice remplacé:
écouteur modifié:
la source
si vous devez recréer une activité à la volée, par exemple: changer de thème, un simple drapeau / compteur ne fonctionnera pas
utiliser la fonction onUserInteraction () pour détecter l'activité de l'utilisateur,
référence: https://stackoverflow.com/a/25070696/4772917
la source
J'ai fait de la manière la plus simple:
onCreate ();
Terminé
la source
la source
C'est ma solution finale et facile à utiliser:
Utilisez la valeur
setSelection(...)
par défaut pour le comportement par défaut ou utilisezsetSelectionWithoutInformListener(...)
pour sélectionner un élément dans le spinner sans déclencher le rappel OnItemSelectedListener.la source
Je dois utiliser
mSpinner
dans ViewHolder, donc l'indicateurmOldPosition
est défini dans la classe interne anonyme.la source
Je voudrais stocker l'index initial lors de la création de l'objet onClickListener.
la source
Ma solution utilise
onTouchListener
mais ne restreint pas son utilisation. Il crée un wrapper pouronTouchListener
si nécessaire où l'installationonItemSelectedListener
.la source
Je réponds peut-être trop tard au cours de la publication, mais j'ai réussi à y parvenir en utilisant la bibliothèque de liaison de données Android Android Databinding . J'ai créé une liaison personnalisée pour m'assurer que l'écouteur n'est pas appelé jusqu'à ce que l'élément sélectionné soit modifié, même si l'utilisateur sélectionne la même position encore et encore, l'événement n'est pas déclenché.
Fichier xml de mise en page
app:position
est l'endroit où vous passez la position à sélectionner.Reliure personnalisée
Vous pouvez en savoir plus sur la liaison de données personnalisées ici Android Setter personnalisé
REMARQUE
N'oubliez pas d'activer la liaison de données dans votre fichier Gradle
Inclure vos fichiers de mise en page dans les
<layout>
balisesla source
la source