Ich habe mir weniger elegante Wege ausgedacht, um das zu lösen, aber ich weiß, dass mir etwas fehlen muss.
Mein onItemSelected
Feuer wird sofort ohne Interaktion mit dem Benutzer ausgelöst, und dies ist ein unerwünschtes Verhalten. Ich möchte, dass die Benutzeroberfläche wartet, bis der Benutzer etwas auswählt, bevor er etwas tut.
Ich habe sogar versucht, den Listener in der einzurichten, in der onResume()
Hoffnung, dass das helfen würde, aber das tut es nicht.
Wie kann ich verhindern, dass dies ausgelöst wird, bevor der Benutzer die Steuerung berühren kann?
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
quelle
quelle
Spinner
leer zu machen und darin zuonItemSelected
erkennen, ob der String dann nicht leer iststartActivity
!Antworten:
Ich hätte erwartet, dass Ihre Lösung funktioniert - ich dachte, das Auswahlereignis würde nicht ausgelöst, wenn Sie den Adapter vor dem Einrichten des Listeners einstellen.
Abgesehen davon würde ein einfaches boolesches Flag es Ihnen ermöglichen, das erste Auswahlereignis eines Schurken zu erkennen und es zu ignorieren.
quelle
onResume()
und zu erfolgen scheint undonPostResume()
alle normalen Hooks zum Zeitpunkt des Layouts abgeschlossen sind.Die Verwendung von Runnables ist völlig falsch.
Verwenden Sie
setSelection(position, false);
in der ersten Auswahl vorsetOnItemSelectedListener(listener)
Auf diese Weise stellen Sie Ihre Auswahl ohne Animation ein, wodurch der Listener für das ausgewählte Element aufgerufen wird. Der Listener ist jedoch null, sodass nichts ausgeführt wird. Dann wird Ihr Hörer zugewiesen.
Befolgen Sie also genau diese Reihenfolge:
quelle
Versuchen Sie unter Bezugnahme auf die Antwort von Dan Dyer, die
OnSelectListener
in einerpost(Runnable)
Methode zu registrieren :Auf diese Weise trat für mich endlich das gewünschte Verhalten auf.
In diesem Fall bedeutet dies auch, dass der Listener nur auf ein geändertes Element feuert.
quelle
onCreate()
,onResume()
usw. In diesem Fall seines fantastischen Tricks, ohne die Gefahr einer Race - Bedingung. Normalerweise verwende ich diesen TrickonCreate()
direkt nach dem Layoutcode.Ich habe eine kleine Dienstprogrammmethode zum Ändern der
Spinner
Auswahl erstellt, ohne den Benutzer zu benachrichtigen:Es deaktiviert den Listener, ändert die Auswahl und aktiviert den Listener danach wieder.
Der Trick besteht darin, dass Aufrufe asynchron zum UI-Thread sind, sodass Sie dies in aufeinanderfolgenden Handler-Posts tun müssen.
quelle
setSpinnerSelectionWithoutCallingListener
zweimal schnell anrufen , sodass der zweite Anruf getätigt wird, während der erste den Hörer bereits eingestellt hatnull
, bleibt Ihr Spinner für immer bei einemnull
Hörer. Ich schlage vor , das folgende Update: addif (listener == null) return;
nachspinner.setSelection(selection)
.Leider scheinen die beiden am häufigsten vorgeschlagenen Lösungen für dieses Problem, nämlich das Zählen von Rückrufereignissen und das Posten eines Runnable zum späteren Festlegen des Rückrufs, beide fehlschlagen zu können, wenn beispielsweise Eingabehilfen aktiviert sind. Hier ist eine Hilfsklasse, die diese Probleme umgeht. Weitere Erläuterungen finden Sie im Kommentarblock.
quelle
Ich hatte viele Probleme mit dem Spinnerfeuer, als ich nicht wollte, und alle Antworten hier sind unzuverlässig. Sie funktionieren - aber nur manchmal. Sie werden schließlich auf Szenarien stoßen, in denen sie fehlschlagen und Fehler in Ihren Code einführen.
Für mich hat es funktioniert, den zuletzt ausgewählten Index in einer Variablen zu speichern und im Listener auszuwerten. Wenn es mit dem neu ausgewählten Index identisch ist, tun Sie nichts und kehren Sie zurück, andernfalls fahren Sie mit dem Listener fort. Mach das:
Vertrauen Sie mir, wenn ich das sage, das ist bei weitem die zuverlässigste Lösung. Ein Hack, aber es funktioniert!
quelle
Ich war in einer ähnlichen Situation und habe eine einfache Lösung für mich.
Es scheint wie Methoden
setSelection(int position)
undsetSelected(int position, boolean animate)
haben unterschiedliche interne Implementierung.Wenn Sie die zweite Methode
setSelected(int position, boolean animate)
mit falschem Animationsflag verwenden, erhalten Sie die Auswahl, ohne denonItemSelected
Listener auszulösen.quelle
setSelection(int position, boolean animate);
onItemSelected
Leider ruft die falsche Animationsflagge immer noch API23 aufUm die Hinweise zur Verwendung des onTouchListener zur Unterscheidung zwischen automatischen Aufrufen des setOnItemSelectedListener (die Teil der Aktivitätsinitialisierung usw. sind) und Aufrufen des durch die tatsächliche Benutzerinteraktion ausgelösten Aufrufs zu konkretisieren, habe ich Folgendes ausgeführt, nachdem ich hier und hier einige andere Vorschläge ausprobiert hatte fand heraus, dass es mit den wenigsten Codezeilen gut funktionierte.
Legen Sie einfach ein Boolesches Feld für Ihre Aktivität / Ihr Fragment fest:
Bevor Sie den setOnItemSelectedListener Ihres Spinners festlegen, legen Sie einen onTouchListener fest:
quelle
quelle
Nachdem ich mir schon lange die Haare ausgezogen habe, habe ich meine eigene Spinner-Klasse erstellt. Ich habe eine Methode hinzugefügt, die den Listener entsprechend trennt und verbindet.
Verwenden Sie es in Ihrem XML wie folgt:
Alles, was Sie tun müssen, ist, die Instanz von SaneSpinner nach dem Aufblasen abzurufen und die Set-Auswahl wie folgt aufzurufen:
Damit wird kein Ereignis ausgelöst und die Benutzerinteraktion wird nicht unterbrochen. Dies hat meine Codekomplexität stark reduziert. Dies sollte auf Lager Android enthalten sein, da es wirklich eine PITA ist.
quelle
Keine unerwünschten Ereignisse aus der Layoutphase, wenn Sie das Hinzufügen des Listeners verschieben, bis das Layout fertig ist:
quelle
ViewTreeObserver.OnGlobalLayoutListener
On-Versionen unter J durch Aufrufen entfernen können.ViewTreeObserver.removeGlobalOnLayoutListener
Dies ist veraltet und hat einen ähnlichen Namen wie die in dieser Antwort verwendete Methode.Dies geschieht, wenn Sie im Code als auswählen.
Anstelle der obigen Anweisung verwenden
Bearbeiten: Diese Methode funktioniert nicht für Mi Android Version Mi UI.
quelle
Ich habe eine sehr einfache Antwort erhalten, 100% sicher, dass es funktioniert:
quelle
Ich habe eine viel elegantere Lösung dafür gefunden. Dabei wird gezählt, wie oft der ArrayAdapter (in Ihrem Fall "Adapter") aufgerufen wurde. Angenommen, Sie haben 1 Spinner und rufen an:
Deklarieren Sie einen int-Zähler nach der onCreate-Methode und geben Sie dann innerhalb der onItemSelected () -Methode eine "if" -Bedingung ein, um zu überprüfen, wie oft der atapter aufgerufen wurde. In Ihrem Fall haben Sie es nur einmal so genannt:
quelle
Mein kleiner Beitrag ist eine Variation einiger der oben genannten Punkte, die mir einige Male gefallen hat.
Deklarieren Sie eine Ganzzahlvariable als Standardwert (oder als zuletzt verwendeten Wert, der in den Einstellungen gespeichert wurde). Verwenden Sie spinner.setSelection (myDefault), um diesen Wert festzulegen, bevor der Listener registriert wird. Überprüfen Sie in onItemSelected, ob der neue Spinner-Wert dem von Ihnen zugewiesenen Wert entspricht, bevor Sie weiteren Code ausführen.
Dies hat den zusätzlichen Vorteil, dass kein Code ausgeführt wird, wenn der Benutzer denselben Wert erneut auswählt.
quelle
Nachdem ich das gleiche Problem hatte, kam ich mit Tags zu diesen Lösungen. Die Idee dahinter ist einfach: Wenn der Spinner programmgesteuert geändert wird, stellen Sie sicher, dass das Tag die ausgewählte Position widerspiegelt. Im Listener prüfen Sie dann, ob die ausgewählte Position dem Tag entspricht. In diesem Fall wurde die Auswahl der Drehfelder programmgesteuert geändert.
Unten ist meine neue "Spinner Proxy" Klasse:
Sie benötigen außerdem eine XML-Datei mit dem Tag-Setup in Ihrem
Values
Verzeichnis. Ich habe meine Datei benanntspinner_tag.xml
, aber das liegt bei Ihnen. Es sieht aus wie das:Jetzt ersetzen
in Ihrem Code mit
Und lassen Sie Ihren Handler so aussehen:
Die Funktion
isUiTriggered()
gibt nur dann true zurück, wenn der Spinner vom Benutzer geändert wurde. Beachten Sie, dass diese Funktion einen Nebeneffekt hat - sie setzt das Tag, sodass ein zweiter Aufruf im selben Listener-Aufruf immer zurückkehrtfalse
.Dieser Wrapper behandelt auch das Problem, dass der Listener während der Layouterstellung aufgerufen wird.
Viel Spaß, Jens.
quelle
Da bei mir nichts funktioniert hat und ich mehr als einen Spinner in meiner Ansicht habe (und IMHO, der eine Bool-Map hält, ist ein Overkill), benutze ich das Tag, um die Klicks zu zählen:
quelle
Viele Antworten schon, hier ist meine.
Ich erweitere
AppCompatSpinner
und füge eine Methode hinzupgmSetSelection(int pos)
, die eine programmatische Auswahleinstellung ermöglicht, ohne einen Auswahlrückruf auszulösen. Ich habe dies mit RxJava codiert, damit die Auswahlereignisse über eine übermittelt werdenObservable
.Ein Beispiel für seine Verwendung, das
onCreateView()
in einemFragment
Beispiel aufgerufen wird :wo
setSelection()
ist ein Verfahren , in der umschließenden Ansicht , die so aussieht, und die von beiden Benutzerauswahlen Ereignissen über die aufgerufen wird , anObservable
anderer Stelle programmatisch und auch, so die Logik für die Auswahl der Handhabung zu beiden Auswahlmethoden ist weit verbreitet.quelle
Ich würde versuchen anzurufen
nachdem Sie setAdapter () aufgerufen haben. Versuchen Sie auch, vor dem Adapter anzurufen.
Sie haben immer die Lösung für die Unterklasse, bei der Sie ein boolesches Flag in Ihre überschriebene setAdapter-Methode einschließen können, um das Ereignis zu überspringen.
quelle
Die Lösung mit einem booleschen Flag oder einem Zähler hat mir nicht geholfen, da onItemSelected () während der Orientierungsänderung das Flag oder den Zähler "überflog".
Ich habe eine Unterklasse erstellt
android.widget.Spinner
und winzige Ergänzungen vorgenommen. Die relevanten Teile sind unten. Diese Lösung hat bei mir funktioniert.quelle
Dies ist auch keine elegante Lösung. Eigentlich ist es eher Rube-Goldberg, aber es scheint zu funktionieren. Ich stelle sicher, dass der Spinner mindestens einmal verwendet wurde, indem ich den Array-Adapter erweitere und seine getDropDownView überschreibe. In der neuen Methode getDropDownView habe ich ein boolesches Flag gesetzt, das anzeigt, dass das Dropdown-Menü mindestens einmal verwendet wurde. Ich ignoriere Anrufe an den Listener, bis das Flag gesetzt ist.
MainActivity.onCreate ():
Array-Adapter überschreiben:
modifizierter Listener:
quelle
Wenn Sie Aktivitäten im laufenden Betrieb neu erstellen müssen, z. B. das Ändern von Themen, funktioniert eine einfache Flagge / ein einfacher Zähler nicht
Verwenden Sie die Funktion onUserInteraction (), um Benutzeraktivitäten zu erkennen.
Referenz: https://stackoverflow.com/a/25070696/4772917
quelle
Ich habe es auf einfachste Weise gemacht:
onCreate ();
Erledigt
quelle
quelle
Das ist meine endgültige und einfach zu verwendende Lösung:
Verwenden Sie die Standardeinstellung
setSelection(...)
für das Standardverhalten oder diesetSelectionWithoutInformListener(...)
Auswahl eines Elements im Drehfeld, ohne den OnItemSelectedListener-Rückruf auszulösen.quelle
Ich muss
mSpinner
in ViewHolder verwenden, damit das FlagmOldPosition
in der anonymen inneren Klasse gesetzt wird.quelle
Ich würde den anfänglichen Index während der Erstellung des onClickListener-Objekts speichern.
quelle
Meine Lösung verwendet
onTouchListener
, beschränkt sich jedoch nicht auf ihre Verwendung. Es wirdonTouchListener
bei Bedarf ein Wrapper für die Einrichtung erstelltonItemSelectedListener
.quelle
Ich antworte möglicherweise zu spät über den Beitrag, aber ich habe es geschafft, dies mithilfe der Android-Datenbindungsbibliothek Android Databinding zu erreichen . Ich habe eine benutzerdefinierte Bindung erstellt, um sicherzustellen, dass der Listener erst aufgerufen wird, wenn das ausgewählte Element geändert wird. Selbst wenn der Benutzer immer wieder dieselbe Position auswählt, wird das Ereignis nicht ausgelöst.
Layout-XML-Datei
app:position
Hier übergeben Sie die zu wählende Position.Kundenspezifische Bindung
Weitere Informationen zur benutzerdefinierten Datenbindung finden Sie hier Android Custom Setter
HINWEIS
Vergessen Sie nicht, die Datenbindung in Ihrer Gradle-Datei zu aktivieren
Fügen Sie Ihre Layoutdateien in
<layout>
Tags einquelle
quelle