Ich frage mich nur, wie Sie mit dem folgenden Problem umgehen: Ein Ergebnis wird abhängig von zwei ausgewählten Spinnern berechnet. Um mit den UI-Dingen umzugehen, d. H. Ein Benutzer wählt ein neues Element in einem der Spinner aus. Ich installiere einen Listener mit setOnItemSelectedListener
für den Spinner in meiner onCreate()
-Methode der Aktivität.
Nun, das funktioniert natürlich gut. Die Arbeit des Hörers besteht darin, eine neue Berechnung des Ergebnisses auszulösen.
Das Problem: Weil ich onPause()
onResume()
abfange, um den letzten Status zu speichern/wiederherzustellen, habe ich eine Methode erhalten, die setzt das ausgewählte Element dieser beiden Spinner programmgesteuert wie hier:
startSpinner.setSelection(pStart);
destSpinner.setSelection(pDest);
Diese beiden Anrufe rufen auch die Zuhörer auf! Meine Berechnungsmethode für das Ergebnis sowie die Benachrichtigung über eine neue Ergebnismenge wird hier zweimal aufgerufen.
Ein blöder direkter Ansatz für dieses Vorgehen wäre, eine boolesche Variable zu haben, die deaktiviert, was auch immer der Listener im Inneren tut, diese zu setzen, bevor die ausgewählten Elemente gesetzt und danach zurückgesetzt werden. Aber gibt es eine bessere Methode?
Ich möchte nicht, dass Listener durch Code aufgerufen werden - Aktionen, nur durch Benutzeraktionen! :-(
Wie machst du das? Danke!
Ich habe eine einfachere und meiner Meinung nach bessere Lösung. Da ich die Spinner auch nach der Initialisierung auffrischen musste, ist dies ein generischer Ansatz.
Eine sauberere Lösung, um zwischen programmatischen und vom Benutzer initiierten Änderungen zu unterscheiden, ist meiner Meinung nach die folgende:
Erstellen Sie den Listener für den Spinner als OnTouchListener und OnItemSelectedListener
public class SpinnerInteractionListener implements AdapterView.OnItemSelectedListener, View.OnTouchListener {
boolean userSelect = false;
@Override
public boolean onTouch(View v, MotionEvent event) {
userSelect = true;
return false;
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
if (userSelect) {
// Your selection handling code here
userSelect = false;
}
}
}
Fügen Sie den Listener dem Spinner hinzu, der sich für beide Ereignistypen registriert
SpinnerInteractionListener listener = new SpinnerInteractionListener();
mSpinnerView.setOnTouchListener(listener);
mSpinnerView.setOnItemSelectedListener(listener);
Auf diese Weise werden unerwartete Aufrufe Ihrer Handler-Methode aufgrund der Initialisierung oder Neuinitialisierung ignoriert.
Okay, ich habe es so laufen lassen, wie ich es jetzt möchte.
Die Sache, die hier zu verstehen ist (und ich habe es nicht getan, als ich diese Frage schrieb ...) ist, dass alles in Android in einem Thread läuft - dem UI-Thread.
Das bedeutet: Auch wenn Sie die Werte von Spinner hier und dort einstellen: Sie werden nur aktualisiert (visuell) und ihre Zuhörer werden nur als after alle Methoden bezeichnet, in denen Sie sich gerade befinden (wie onCreate
, onResume
oder was auch immer) fertig.
Dies ermöglicht Folgendes:
currentPos1
, currentPos2
)onItemSelectedListener()
rufen eine Methode wie refreshMyResult()
oder wie auch immer auf.Die refreshMyResult()
-Methode sieht folgendermaßen aus:
int newPos1 = mySpinner1.getSelectedItemPosition();
int newPos2 = mySpinner2.getSelectedItemPosition();
// only do something if update is not done yet
if (newPos1 != currentPos1 || newPos2 != currentPos2) {
currentPos1 = newPos1;
currentPos2 = newPos2;
// do whatever has to be done to update things!
}
Da die Listener später aufgerufen werden - und bis dahin ist die gespeicherte Position in currentPos bereits aktualisiert - wird nichts geschehen und es wird keine unnötige Aktualisierung von irgendetwas anderem stattfinden .. Wenn ein Benutzer einen neuen Wert in einem der Spinner auswählt na ja - das Update wird entsprechend durchgeführt!
Das ist es! :-)
Ahh - noch etwas: Die Antwort auf meine Frage lautet: Nein. Die Hörer können nicht (einfach) deaktiviert werden und werden aufgerufen, wenn ein Wert geändert wird.
Fügen Sie zunächst boolesche Werte hinzu, um den Listener-Aufruf von Spinner zu stoppen
Boolean check = false;
Dann fügen Sie einen Touch-Listener hinzu, und klicken Sie unter Artikel auf Listener Like
holder.filters.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
check = true;
return false;
}
});
holder.filters.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener()
{
@Override
public void onItemSelected(AdapterView<?> parent, View arg1, int position, long id)
{
flag = filterids.get(position);
if(check)
{
check = false;
new Applyfilters().execute(flag,"3");
}else{
}
}
@Override
public void onNothingSelected(AdapterView<?> arg0)
{
// TODO Auto-generated method stub
}
});
Es ist einfach, um Serveranrufe mehrmals zu stoppen.
Es ist sehr einfach, die Spinner.setSelection(int position, boolean animate)
-Methode mit false
aufzurufen, damit die Listener nicht auf die Änderung reagieren.
Spinner.setSelection (int position, boolean animate) löst den Listener auf 4.3 aus
Ich habe eine Bibliothek erstellt, die allen hilft, das Element nicht mit der Aktion "Clicken" in Spinner .__ aufzurufen. Zum Beispiel:
spinner.setSelection(withAction,position);
wobei withAction ein boolesches Flag ist, das für die Aufruf- oder Nicht-Elementaktion verwendet wird
Link auf Github: https://github.com/scijoker/spinner2
Wenn Spinner.setSelection (Position) verwendet wird, wird immer setOnItemSelectedListener () aktiviert.
Um zu vermeiden, dass der Code zweimal ausgelöst wird, verwende ich diese Lösung:
private mIsSpinnerFirstCall=true;
...
Spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
//If a new value is selected (avoid activating on setSelection())
if(!mIsSpinnerFirstCall) {
// Your code goes gere
}
mIsSpinnerFirstCall = false;
}
public void onNothingSelected(AdapterView<?> arg0) {
}
});
Diese Lösung ist gültig, wenn Sie sicher sind, dass Spinner.setSelection (Position) verwendet wurde. Es ist auch wichtig, vor der Verwendung von Spinner.setSelection (Position) jedes Mal mIsSpinnerFirstCall = true festzulegen.
Fügen Sie die OnItemSelectedListener
für jeden Spinner after hinzu, den Sie in onResume
zuvor festgelegt haben.
Meine Lösung ist sehr einfach. Initialisieren Sie zuerst eine globale boolesche Variable.
boolean shouldWork = true;
Verwenden Sie anschließend den folgenden Code in Ihrer onCreate () - Methode.
Spinner spinner = findViewById(R.id.spinner);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView adapter, View v, int i, long lng) {
if (shouldWork) {
// Do your actions here
}
else
shouldWork = true;
}
public void onNothingSelected(AdapterView<?> parentView) {
}
});
Jetzt können Sie die Methode setSelection in everwhere verwenden, ohne die Methode onItemSelected () mit dem folgenden Code aufzurufen.
shouldWork = false;
spinner.setSelection(0);
This following method will help you to stop invoking automatically the selection listener
yourspinnerobj.post(new Runnable() {
@Override
public void run() {
yourspinnerobj.setOnItemSelectedListener(yourspinnerlistener);
}
});