it-swarm.com.de

Wie kann man verhindern, dass onItemSelected mit einem neu instanziierten Spinner startet?

Ich habe an einige weniger elegante Wege gedacht, um dieses Problem zu lösen, aber ich weiß, dass mir etwas fehlt.

Meine onItemSelected 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 onResume() einzurichten, in der Hoffnung, dass dies helfen würde, aber es funktioniert nicht.

Wie kann ich verhindern, dass dies ausgelöst wird, bevor der Benutzer das Steuerelement 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.
    }
}
}
386
FauxReal

Ich hätte erwartet, dass Ihre Lösung funktioniert - ich würde zwar das Auswahlereignis nicht auslösen, wenn Sie den Adapter vor dem Einrichten des Listeners einstellen.

Eine einfache boolesche Flagge würde es Ihnen jedoch ermöglichen, das erste Auswahlereignis der Schurken zu erkennen und zu ignorieren.

71
CommonsWare

Die Verwendung von Runnables ist völlig falsch. 

Verwenden Sie setSelection(position, false); in der ersten Auswahl vor setOnItemSelectedListener(listener) 

Auf diese Weise setzen Sie Ihre Auswahl ohne Animation, wodurch der auf Element ausgewählte Listener aufgerufen wird. Der Listener ist jedoch null, sodass nichts ausgeführt wird. Dann wird Ihr Listener zugewiesen. 

Folge also genau dieser Reihenfolge: 

Spinner s = (Spinner)Util.findViewById(view, R.id.sound, R.id.spinner);
s.setAdapter(adapter);
s.setSelection(position, false);
s.setOnItemSelectedListener(listener);
355
Brad

Versuchen Sie unter Bezugnahme auf die Antwort von Dan Dyer, die Variable OnSelectListener in einer post(Runnable)-Methode zu registrieren:

spinner.post(new Runnable() {
    public void run() {
        spinner.setOnItemSelectedListener(listener);
    }
});

Damit ist für mich das gewünschte Verhalten endlich eingetreten.

In diesem Fall bedeutet dies auch, dass der Listener nur bei einem geänderten Element ausgelöst wird.

187
casaflowa

Ich habe eine kleine Hilfsmethode erstellt, um die Spinner-Auswahl zu ändern, ohne den Benutzer zu benachrichtigen:

private void setSpinnerSelectionWithoutCallingListener(final Spinner spinner, final int selection) {
    final OnItemSelectedListener l = spinner.getOnItemSelectedListener();
    spinner.setOnItemSelectedListener(null);
    spinner.post(new Runnable() {
        @Override
        public void run() {
            spinner.setSelection(selection);
            spinner.post(new Runnable() {
                @Override
                public void run() {
                    spinner.setOnItemSelectedListener(l);
                }
            });
        }
    });
}

Es deaktiviert den Listener, ändert die Auswahl und aktiviert den Listener danach wieder.

Der Trick ist, dass Aufrufe asynchron zum UI-Thread sind, also müssen Sie dies in aufeinanderfolgenden Handler-Posts tun.

47
karooolek

Leider scheint es, dass die beiden am häufigsten vorgeschlagenen Lösungen für dieses Problem, nämlich das Zählen von Callback-Vorkommen und das Einrichten eines Runnable-Objekts, um den Callback zu einem späteren Zeitpunkt festzulegen, beide fehlschlagen, wenn beispielsweise die Eingabehilfen aktiviert sind. Hier ist eine Hilfsklasse, die diese Probleme umgeht. Weitere Erläuterungen finden Sie im Kommentarblock.

import Android.view.View;
import Android.widget.AdapterView;
import Android.widget.AdapterView.OnItemSelectedListener;
import Android.widget.Spinner;
import Android.widget.SpinnerAdapter;

/**
 * Spinner Helper class that works around some common issues 
 * with the stock Android Spinner
 * 
 * A Spinner will normally call it's OnItemSelectedListener
 * when you use setSelection(...) in your initialization code. 
 * This is usually unwanted behavior, and a common work-around 
 * is to use spinner.post(...) with a Runnable to assign the 
 * OnItemSelectedListener after layout.
 * 
 * If you do not call setSelection(...) manually, the callback
 * may be called with the first item in the adapter you have 
 * set. The common work-around for that is to count callbacks.
 * 
 * While these workarounds usually *seem* to work, the callback
 * may still be called repeatedly for other reasons while the 
 * selection hasn't actually changed. This will happen for 
 * example, if the user has accessibility options enabled - 
 * which is more common than you might think as several apps 
 * use this for different purposes, like detecting which 
 * notifications are active.
 * 
 * Ideally, your OnItemSelectedListener callback should be
 * coded defensively so that no problem would occur even
 * if the callback was called repeatedly with the same values
 * without any user interaction, so no workarounds are needed.
 * 
 * This class does that for you. It keeps track of the values
 * you have set with the setSelection(...) methods, and 
 * proxies the OnItemSelectedListener callback so your callback
 * only gets called if the selected item's position differs 
 * from the one you have set by code, or the first item if you
 * did not set it.
 * 
 * This also means that if the user actually clicks the item
 * that was previously selected by code (or the first item
 * if you didn't set a selection by code), the callback will 
 * not fire.
 * 
 * To implement, replace current occurrences of:
 * 
 *     Spinner spinner = 
 *         (Spinner)findViewById(R.id.xxx);
 *     
 * with:
 * 
 *     SpinnerHelper spinner = 
 *         new SpinnerHelper(findViewById(R.id.xxx))
 *         
 * SpinnerHelper proxies the (my) most used calls to Spinner
 * but not all of them. Should a method not be available, use: 
 * 
 *      spinner.getSpinner().someMethod(...)
 *
 * Or just add the proxy method yourself :)
 * 
 * (Quickly) Tested on devices from 2.3.6 through 4.2.2
 * 
 * @author Jorrit "Chainfire" Jongma
 * @license WTFPL (do whatever you want with this, nobody cares)
 */
public class SpinnerHelper implements OnItemSelectedListener {
    private final Spinner spinner;

    private int lastPosition = -1;
    private OnItemSelectedListener proxiedItemSelectedListener = null;  

    public SpinnerHelper(Object spinner) {
         this.spinner = (spinner != null) ? (Spinner)spinner : null;        
    }

    public Spinner getSpinner() {
        return spinner;
    }

    public void setSelection(int position) { 
        lastPosition = Math.max(-1, position);
        spinner.setSelection(position);     
    }

    public void setSelection(int position, boolean animate) {
        lastPosition = Math.max(-1, position);
        spinner.setSelection(position, animate);        
    }

    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
        proxiedItemSelectedListener = listener;
        spinner.setOnItemSelectedListener(listener == null ? null : this);
    }   

    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        if (position != lastPosition) {
            lastPosition = position;
            if (proxiedItemSelectedListener != null) {
                proxiedItemSelectedListener.onItemSelected(
                        parent, view, position, id
                );
            }
        }
    }

    public void onNothingSelected(AdapterView<?> parent) {
        if (-1 != lastPosition) {
            lastPosition = -1;
            if (proxiedItemSelectedListener != null) {
                proxiedItemSelectedListener.onNothingSelected(
                        parent
                );
            }
        }
    }

    public void setAdapter(SpinnerAdapter adapter) {
        if (adapter.getCount() > 0) {
            lastPosition = 0;
        }
        spinner.setAdapter(adapter);
    }

    public SpinnerAdapter getAdapter() { return spinner.getAdapter(); } 
    public int getCount() { return spinner.getCount(); }    
    public Object getItemAtPosition(int position) { return spinner.getItemAtPosition(position); }   
    public long getItemIdAtPosition(int position) { return spinner.getItemIdAtPosition(position); }
    public Object getSelectedItem() { return spinner.getSelectedItem(); }
    public long getSelectedItemId() { return spinner.getSelectedItemId(); }
    public int getSelectedItemPosition() { return spinner.getSelectedItemPosition(); }
    public void setEnabled(boolean enabled) { spinner.setEnabled(enabled); }
    public boolean isEnabled() { return spinner.isEnabled(); }
}
33
Jorrit

Ich hatte viele Probleme mit dem Spinner, als ich nicht wollte, und alle Antworten hier sind unzuverlässig. Sie arbeiten - aber nur manchmal. Sie werden schließlich Szenarien kennenlernen, in denen sie versagen und Fehler in Ihren Code einfügen. 

Für mich funktionierte es, den zuletzt ausgewählten Index in einer Variablen zu speichern und im Listener auszuwerten. Wenn es dem neuen ausgewählten Index entspricht, nichts tun und zurückkehren, andernfalls mit dem Listener fortfahren. Mach das:

//Declare a int member variable and initialize to 0 (at the top of your class)
private int mLastSpinnerPosition = 0;

//then evaluate it in your listener
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {

  if(mLastSpinnerPosition == i){
        return; //do nothing
  }

  mLastSpinnerPosition = i;
  //do the rest of your code now

}

Vertrauen Sie mir, wenn ich das sage, dies ist bei weitem die zuverlässigste Lösung. Ein Hack, aber es funktioniert!

29
Chris

Ich war in einer ähnlichen Situation und habe eine einfache Lösung für mich.

Es scheint, als hätten die Methoden setSelection(int position) und setSelected(int position, boolean animate) unterschiedliche interne Implementierungen.

Wenn Sie die zweite Methode setSelected(int position, boolean animate) mit false animate flag verwenden, erhalten Sie die Auswahl, ohne onItemSelected Listener auszulösen.

25
Michal

Um nur einige Hinweise zu geben, wie der onTouchListener verwendet werden kann, um zwischen automatischen Aufrufen des setOnItemSelectedListener (die Teil der Aktivitätsinitialisierung usw. sind) und Aufrufen, die durch tatsächliche Benutzerinteraktion ausgelöst werden, zu unterscheiden, habe ich folgende Schritte ausgeführt, nachdem ich hier und hier einige andere Vorschläge ausprobiert hatte stellte fest, dass es mit den wenigsten Codezeilen gut funktionierte.

Setzen Sie einfach ein boolesches Feld für Ihre Aktivität/Ihr Fragment wie folgt:

private Boolean spinnerTouched = false;

Dann, kurz bevor Sie setOnItemSelectedListener Ihres Spinners setzen, setzen Sie einen onTouchListener:

    spinner.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            System.out.println("Real touch felt.");
            spinnerTouched = true;
            return false;
        }
    });

    spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    ...
         if (spinnerTouched){
         //Do the stuff you only want triggered by real user interaction.
        }
        spinnerTouched = false;
17
spinner.setSelection(Adapter.NO_SELECTION, false);
12
j2emanue

Nachdem ich mir lange Zeit die Haare ausgezogen habe, habe ich meine eigene Spinnerklasse erstellt. Ich habe eine Methode hinzugefügt, die den Listener entsprechend trennt und verbindet.

public class SaneSpinner extends Spinner {
    public SaneSpinner(Context context) {
        super(context);
    }

    public SaneSpinner(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public SaneSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    // set the ceaseFireOnItemClickEvent argument to true to avoid firing an event
    public void setSelection(int position, boolean animate, boolean ceaseFireOnItemClickEvent) {
        OnItemSelectedListener l = getOnItemSelectedListener();
        if (ceaseFireOnItemClickEvent) {
            setOnItemSelectedListener(null);
        }

        super.setSelection(position, animate);

        if (ceaseFireOnItemClickEvent) {
            setOnItemSelectedListener(l);
        }
    }
}

Verwenden Sie es in Ihrem XML folgendermaßen:

<my.package.name.SaneSpinner
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:id="@+id/mySaneSpinner"
    Android:entries="@array/supportedCurrenciesFullName"
    Android:layout_weight="2" />

Alles was Sie tun müssen, ist die Instanz von SaneSpinner nach der Inflation abzurufen und die Anrufsatzauswahl wie folgt abzurufen: 

mMySaneSpinner.setSelection(1, true, true);

Damit wird kein Ereignis ausgelöst und die Benutzerinteraktion wird nicht unterbrochen. Dies hat meine Code-Komplexität stark reduziert. Dies sollte in Android-Stock enthalten sein, da es wirklich eine PITA ist.

8
fusion44

Keine unerwünschten Ereignisse aus der Layoutphase, wenn Sie das Hinzufügen des Listeners bis zum Abschluss des Layouts verschieben

spinner.getViewTreeObserver().addOnGlobalLayoutListener(
    new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            // Ensure you call it only once works for JELLY_BEAN and later
            spinner.getViewTreeObserver().removeOnGlobalLayoutListener(this);

            // add the listener
            spinner.setOnItemSelectedListener(new OnItemSelectedListener() {

                @Override
                public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
                    // check if pos has changed
                    // then do your work
                }

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

            });

        }
    });
7
redocoder

Ich habe eine sehr einfache Antwort erhalten, 100% sicher, dass es funktioniert:

boolean Touched=false; // this a a global variable

public void changetouchvalue()
{
   Touched=true;
}

// this code is written just before onItemSelectedListener

 spinner.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            System.out.println("Real touch felt.");
            changetouchvalue();
            return false;
        }
    });

//inside your spinner.SetonItemSelectedListener , you have a function named OnItemSelected iside that function write the following code

if(Touched)
{
 // the code u want to do in touch event
}
4
user6656805

Ich habe dafür eine viel elegantere Lösung gefunden. Dabei wird gezählt, wie oft der ArrayAdapter (in Ihrem Fall "Adapter") aufgerufen wurde. Nehmen wir an, Sie haben 1 Spinner und rufen an:

int iCountAdapterCalls = 0;

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

Deklarieren Sie einen int-Zähler nach dem onCreate und setzen Sie dann in der Methode onItemSelected () eine "if" - Bedingung, um zu überprüfen, wie oft der atapter aufgerufen wurde. In deinem Fall hast du es nur einmal so angerufen:

if(iCountAdapterCalls < 1)
{
  iCountAdapterCalls++;
  //This section executes in onCreate, during the initialization
}
else
{
  //This section corresponds to user clicks, after the initialization
}
3
g00dy

Dies geschieht, wenn Sie im Code eine Auswahl treffen als;

   mSpinner.setSelection(0);

Anstelle der obigen Anweisung verwenden 

   mSpinner.setSelection(0,false);//just simply do not animate it.

Edit: Diese Methode funktioniert nicht für Mi Android Version Mi UI.

2
Uzair

Mein kleiner Beitrag ist eine Variation von einigen der oben genannten, die mir einige Male gefallen hat.

Deklarieren Sie eine Integer-Variable als Standardwert (oder den zuletzt verwendeten Wert in den Voreinstellungen). Verwenden Sie spinner.setSelection (myDefault), um diesen Wert festzulegen, bevor der Listener registriert wird. Überprüfen Sie in der Option onItemSelected, ob der neue Spinnerwert vorhanden ist entspricht dem Wert, den Sie zugewiesen haben, 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.

1
David Walton

Nachdem ich das gleiche Problem hatte, kam ich zu den Lösungen mit den Tags .. Die Idee dahinter ist einfach: Wenn der Spinner programmgesteuert geändert wird, vergewissern Sie sich, dass das Tag die ausgewählte Position widerspiegelt. Dann überprüfen Sie im Listener, ob die ausgewählte Position dem Tag entspricht. Wenn dies der Fall ist, wurde die Spinnerauswahl programmgesteuert geändert.

Hier ist meine neue "Spinner Proxy" Klasse: 

package com.samplepackage;

import com.samplepackage.R;
import Android.widget.Spinner;

public class SpinnerFixed {

    private Spinner mSpinner;

    public SpinnerFixed(View spinner) {
         mSpinner = (Spinner)spinner;
         mSpinner.setTag(R.id.spinner_pos, -2);
    }

    public boolean isUiTriggered() {
         int tag = ((Integer)mSpinner.getTag(R.id.spinner_pos)).intValue();
         int pos = mSpinner.getSelectedItemPosition();
         mSpinner.setTag(R.id.spinner_pos, pos);
         return (tag != -2 && tag != pos);
    }

    public void setSelection(int position) {
        mSpinner.setTag(R.id.spinner_pos, position);
        mSpinner.setSelection(position);
    }

    public void setSelection(int position, boolean animate) {
        mSpinner.setTag(R.id.spinner_pos, position);
        mSpinner.setSelection(position, animate);
    }

    // If you need to proxy more methods, use "Generate Delegate Methods"
    // from the context menu in Eclipse.
}

Sie benötigen auch eine XML-Datei mit dem Tag-Setup in Ihrem Values-Verzeichnis . Ich habe meine Datei spinner_tag.xml benannt, aber das liegt bei Ihnen . Es sieht so aus:

<resources xmlns:Android="http://schemas.Android.com/apk/res/Android">
  <item name="spinner_pos" type="id" />
</resources>

Jetzt ersetzen

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

in deinem Code mit

SpinnerFixed myspinner;
...
myspinner = new SpinnerFixed(findViewById(R.id.myspinner));

Und lassen Sie Ihren Handler so aussehen:

myspinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        if (myspinner.isUiTriggered()) {
            // Code you want to execute only on UI selects of the spinner
        }
    }

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

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 in demselben Listener-Aufruf immer false zurückgibt.

Dieser Wrapper behandelt auch das Problem mit dem Listener, der während der Layouterstellung aufgerufen wird.

Viel Spaß, Jens.

1
Jens

Da nichts für mich funktionierte und ich mehr als 1 Spinner in meiner Ansicht habe (und IMHO, der eine bool-Karte hält, ist ein Overkill), benutze ich das Tag, um die Klicks zu zählen:

spinner.setTag(0);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            Integer selections = (Integer) parent.getTag();
            if (selections > 0) {
                // real selection
            }
            parent.setTag(++selections); // (or even just '1')
        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {
        }
    });
1
SagiLow

Viele Antworten schon, hier ist meins.

Ich erweitere AppCompatSpinner und füge eine Methode pgmSetSelection(int pos) hinzu, die die Einstellung der programmatischen Auswahl ermöglicht, ohne einen Auswahlrückruf auszulösen. Ich habe dies mit RxJava so codiert, dass die Auswahlereignisse über eine Observable geliefert werden. 

package com.controlj.view;

import Android.content.Context;
import Android.util.AttributeSet;
import Android.view.View;
import Android.widget.AdapterView;

import io.reactivex.Observable;

/**
 * Created by clyde on 22/11/17.
 */

public class FilteredSpinner extends Android.support.v7.widget.AppCompatSpinner {
    private int lastSelection = INVALID_POSITION;


    public void pgmSetSelection(int i) {
        lastSelection = i;
        setSelection(i);
    }

    /**
     * Observe item selections within this spinner. Events will not be delivered if they were triggered
     * by a call to setSelection(). Selection of nothing will return an event equal to INVALID_POSITION
     *
     * @return an Observable delivering selection events
     */
    public Observable<Integer> observeSelections() {
        return Observable.create(emitter -> {
            setOnItemSelectedListener(new OnItemSelectedListener() {
                @Override
                public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                    if(i != lastSelection) {
                        lastSelection = i;
                        emitter.onNext(i);
                    }
                }

                @Override
                public void onNothingSelected(AdapterView<?> adapterView) {
                    onItemSelected(adapterView, null, INVALID_POSITION, 0);
                }
            });
        });
    }

    public FilteredSpinner(Context context) {
        super(context);
    }

    public FilteredSpinner(Context context, int mode) {
        super(context, mode);
    }

    public FilteredSpinner(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FilteredSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public FilteredSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode) {
        super(context, attrs, defStyleAttr, mode);
    }
}

Ein Beispiel für seine Verwendung, in onCreateView() in einer Fragment aufgerufen, zum Beispiel:

    mySpinner = view.findViewById(R.id.history);
    mySpinner.observeSelections()
        .subscribe(this::setSelection);

dabei ist setSelection() eine Methode in der umschließenden Ansicht, die so aussieht und sowohl von Benutzerauswahlereignissen über die Observable als auch von anderen Stellen programmgesteuert aufgerufen wird. Daher ist die Logik für die Auswahl von Auswahlen beiden Selektionsmethoden gemeinsam.

private void setSelection(int position) {
    if(adapter.isEmpty())
        position = INVALID_POSITION;
    else if(position >= adapter.getCount())
        position = adapter.getCount() - 1;
    MyData result = null;
    mySpinner.pgmSetSelection(position);
    if(position != INVALID_POSITION) {
        result = adapter.getItem(position);
    }
    display(result);  // show the selected item somewhere
}
1
Clyde

Ich würde den Anfangsindex während der Erstellung des onClickListener-Objekts speichern.

   int thisInitialIndex = 0;//change as needed

   myspinner.setSelection(thisInitialIndex);

   myspinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

      int initIndex = thisInitialIndex;

      @Override
      public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
         if (id != initIndex) { //if selectedIndex is the same as initial value
            // your real onselecteditemchange event
         }
      }

      @Override
      public void onNothingSelected(AdapterView<?> parent) {
      }
  });
0
Ray Lionfang

Möglicherweise antworte ich zu spät, aber es gelang mir mit der Android-Datenbindungsbibliothek Android Databinding . Ich habe eine benutzerdefinierte Bindung erstellt, um sicherzustellen, dass der Listener nicht aufgerufen wird, bis das ausgewählte Element geändert wird. Auch wenn der Benutzer immer wieder dieselbe Position auswählt, wird das Ereignis nicht ausgelöst.

Layout-XML-Datei

    <layout>
  <RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
 Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:layout_marginTop="@dimen/activity_vertical_margin"
xmlns:app="http://schemas.Android.com/apk/res-auto">


<Spinner
    Android:id="@+id/spinner"
    Android:layout_width="150dp"
    Android:layout_height="wrap_content"
    Android:spinnerMode="dropdown"
    Android:layout_below="@id/member_img"
    Android:layout_marginTop="@dimen/activity_vertical_margin"
    Android:background="@drawable/member_btn"
    Android:padding="@dimen/activity_horizontal_margin"
    Android:layout_marginStart="@dimen/activity_horizontal_margin"
    Android:textColor="@color/colorAccent"
    app:position="@{0}"
    />
 </RelativeLayout>
 </layout>

app:position ist der Ort, an dem Sie die zu wählende Position übergeben.

Benutzerdefinierte Bindung

  @BindingAdapter(value={ "position"}, requireAll=false)
  public static void setSpinnerAdapter(Spinner spinner, int selected) 
  {

    final int [] selectedposition= new int[1];
    selectedposition[0]=selected;


    // custom adapter or you can set default adapter
        CustomSpinnerAdapter customSpinnerAdapter = new CustomSpinnerAdapter(spinner.getContext(), <arraylist you want to add to spinner>);
        spinner.setAdapter(customSpinnerAdapter);
            spinner.setSelection(selected,false);


    spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

            String item = parent.getItemAtPosition(position).toString();
        if( position!=selectedposition[0]) {
                        selectedposition[0]=position;
            // do your stuff here
                    }
                }


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

        }
    });
}

Weitere Informationen zur benutzerdefinierten Datenbindung finden Sie hier Android Custom Setter

HINWEIS

  1. Vergessen Sie nicht, die Datenbindung in Ihrer Gradle-Datei zu aktivieren

       Android {
     ....
     dataBinding {
     enabled = true
    }
    }
    
  2. Fügen Sie Ihre Layoutdateien in <layout>-Tags ein

0
N.Moudgil

Ich muss mSpinner in ViewHolder verwenden, damit das Flag mOldPosition in der anonymen inneren Klasse gesetzt wird.

mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            int mOldPosition = mSpinner.getSelectedItemPosition();

            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long l) {
                if (mOldPosition != position) {
                    mOldPosition = position;
                    //Do something
                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> adapterView) {
                //Do something
            }
        });
0
Francis Bacon

Ich würde versuchen anzurufen

spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());

nachdem Sie setAdapter () aufgerufen haben. Versuchen Sie auch, vor dem Adapter anzurufen.

Sie haben immer die Lösung für die Unterklasse. Sie können ein boolesches Flag in Ihre überschriebene setAdapter-Methode einbetten, um das Ereignis zu überspringen.

0
Pentium10
if () {        
       spinner.setSelection(0);// No reaction to create spinner !!!
     } else {
        spinner.setSelection(intPosition);
     }


spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

         if (position > 0) {
           // real selection
         }

      }

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

     }
});
0
Gennady Kozlov

Die Lösung mit einem booleschen Flag oder einem Zähler hat mir nicht weitergeholfen, da onItemSelected () während der Orientierungsänderung die Flag oder den Zähler "überflog".

Ich habe Android.widget.Spinner untergeordnet und winzige Ergänzungen vorgenommen. Die relevanten Teile sind unten aufgeführt. Diese Lösung hat für mich funktioniert.

private void setHandleOnItemSelected()
{
  final StackTraceElement [] elements = Thread.currentThread().getStackTrace();

  for (int index = 1; index < elements.length; index++)
  {
     handleOnItemSelected = elements[index].toString().indexOf("PerformClick") != -1; //$NON-NLS-1$

     if (handleOnItemSelected)
     {
        break;
     }
  }
}

@Override
public void setSelection(int position, boolean animate)
{
  super.setSelection(position, animate);

  setHandleOnItemSelected();
}

@Override
public void setSelection(int position)
{
  super.setSelection(position);

  setHandleOnItemSelected();
}

public boolean shouldHandleOnItemSelected()
{
  return handleOnItemSelected;
}
0
RobinBobin

Dies ist auch keine elegante Lösung. Tatsächlich ist es eher Rube-Goldberg, aber es scheint zu funktionieren. Ich stelle sicher, dass der Spinner mindestens einmal verwendet wurde, indem der Array-Adapter erweitert und seine getDropDownView überschrieben wird. In der neuen getDropDownView-Methode habe ich ein boolesches Flag, das so eingestellt ist, dass das Dropdown-Menü mindestens einmal verwendet wurde. Ich ignoriere Anrufe an den Listener, bis das Flag gesetzt ist.

MainActivity.onCreate ():

ActionBar ab = getActionBar();
ab.setDisplayShowTitleEnabled(false);
ab.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
ab.setListNavigationCallbacks(null, null);

ArrayList<String> abList = new ArrayList<String>();
abList.add("line 1");
...

ArAd  abAdapt = new ArAd (this
   , Android.R.layout.simple_list_item_1
   , Android.R.id.text1, abList);
ab.setListNavigationCallbacks(abAdapt, MainActivity.this);

array-Adapter überschreiben:

private static boolean viewed = false;
private class ArAd extends ArrayAdapter<String> {
    private ArAd(Activity a
            , int layoutId, int resId, ArrayList<String> list) {
        super(a, layoutId, resId, list);
        viewed = false;
    }
    @Override
    public View getDropDownView(int position, View convertView,
            ViewGroup parent) {
        viewed = true;
        return super.getDropDownView(position, convertView, parent);
    }
}

modifizierter Listener:

@Override
public boolean onNavigationItemSelected(
   int itemPosition, long itemId) {
   if (viewed) {
     ...
   }
   return false;
}
0
steven smith

wenn Sie die Aktivität schnell neu erstellen müssen, z. B. das Ändern von Themen, funktioniert eine einfache Flagge/Zähler nicht

verwenden Sie die Funktion onUserInteraction (), um die Benutzeraktivität zu ermitteln.

referenz: https://stackoverflow.com/a/25070696/4772917

0
dev-gaek

Das ist meine endgültige und einfach zu verwendende Lösung:

public class ManualSelectedSpinner extends Spinner {
    //get a reference for the internal listener
    private OnItemSelectedListener mListener;

    public ManualSelectedSpinner(Context context) {
        super(context);
    }

    public ManualSelectedSpinner(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ManualSelectedSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void setOnItemSelectedListener(@Nullable OnItemSelectedListener listener) {
        mListener = listener;
        super.setOnItemSelectedListener(listener);
    }

    public void setSelectionWithoutInformListener(int position){
        super.setOnItemSelectedListener(null);
        super.setSelection(position);
        super.setOnItemSelectedListener(mListener);
    }

    public void setSelectionWithoutInformListener(int position, boolean animate){
        super.setOnItemSelectedListener(null);
        super.setSelection(position, animate);
        super.setOnItemSelectedListener(mListener);
    }
}

Verwenden Sie den Standardwert setSelection(...) für das Standardverhalten oder verwenden Sie setSelectionWithoutInformListener(...), um ein Element im Spinner auszuwählen, ohne den Callback von OnItemSelectedListener auszulösen.

0
MatPag

Meine Lösung verwendet onTouchListener, schränkt jedoch die Verwendung nicht ein. Falls erforderlich, wird ein Wrapper für onTouchListener erstellt, in dem onItemSelectedListener eingerichtet wird.

public class Spinner extends Android.widget.Spinner {
    /* ...constructors... */

    private OnTouchListener onTouchListener;
    private OnItemSelectedListener onItemSelectedListener;

    @Override
    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
        onItemSelectedListener = listener;
        super.setOnTouchListener(wrapTouchListener(onTouchListener, onItemSelectedListener));
    }

    @Override
    public void setOnTouchListener(OnTouchListener listener) {
        onTouchListener = listener;
        super.setOnTouchListener(wrapTouchListener(onTouchListener, onItemSelectedListener));
    }

    private OnTouchListener wrapTouchListener(final OnTouchListener onTouchListener, final OnItemSelectedListener onItemSelectedListener) {
        return onItemSelectedListener != null ? new OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                Spinner.super.setOnItemSelectedListener(onItemSelectedListener);
                return onTouchListener != null && onTouchListener.onTouch(view, motionEvent);
            }
        } : onTouchListener;
    }
}
0
Dem0n13

Ich habe getan auf einfachste Weise:

private AdapterView.OnItemSelectedListener listener;
private Spinner spinner;

onCreate ();

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

listener = new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> adapterView, View view, int position, long l) {

            Log.i("H - Spinner selected position", position);
        }

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

        }
    };

 spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
            spinner.setOnItemSelectedListener(listener);
        }

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

        }
    });

Erledigt

0
Hiren Patel