608 Stimmen

Wie erstellt man einen Android Spinner mit dem Anfangstext "Select One"?

Ich möchte einen Spinner verwenden, der zunächst (wenn der Benutzer noch keine Auswahl getroffen hat) den Text "Select One" anzeigt. Wenn der Benutzer auf den Spinner klickt, wird die Liste der Elemente angezeigt und der Benutzer wählt eine der Optionen aus. Nachdem der Benutzer eine Auswahl getroffen hat, wird das ausgewählte Element im Spinner anstelle von "Select One" angezeigt.

Ich habe den folgenden Code, um einen Spinner zu erstellen:

String[] items = new String[] {"One", "Two", "Three"};
Spinner spinner = (Spinner) findViewById(R.id.mySpinner);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
            android.R.layout.simple_spinner_item, items);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);

Mit diesem Code wird zunächst das Element "Eins" angezeigt. Ich könnte einfach ein neues Element "Select One" zu den Elementen hinzufügen, aber dann würde "Select One" auch in der Dropdown-Liste als erstes Element angezeigt werden, was ich nicht möchte.

Wie kann ich dieses Problem beheben?

7 Stimmen

Die perfekte Lösung liegt in dieser Frage: stackoverflow.com/questions/9863378/ Überschreiben Sie einfach die Methode getDropDownView().

0 Stimmen

Haben Sie versucht, das erste Element Ihres Adapters auf "Select One" einzustellen?

0 Stimmen

[Hier eine andere tolle, schöne Lösung!][1] [1]: stackoverflow.com/questions/9863378/

313voto

aaronvargas Punkte 10019

Was Sie tun können, ist die Dekoration Ihrer SpinnerAdapter mit einer, die eine 'Select Option...' präsentiert Ansicht, damit der Spinner zunächst ohne Auswahl angezeigt wird.

Hier ist ein funktionierendes Beispiel für Android 2.3 und 4.0 getestet (es verwendet nichts in der Kompatibilitätsbibliothek, so sollte es für eine Weile in Ordnung sein) Da es ein Dekorator ist, sollte es einfach sein, bestehenden Code nachzurüsten und es funktioniert gut mit CursorLoader s auch. (Tauschen Sie den Cursor auf dem umwickelten cursorAdapter natürlich...)

Es gibt einen Android-Fehler, der die Wiederverwendung von Ansichten etwas erschwert. (Sie müssen also die setTag oder etwas anderes, um sicherzustellen, dass Ihr convertView ist korrekt). Spinner unterstützt nicht mehrere Ansichtstypen

Anmerkungen zum Code: 2 Konstrukteure

Auf diese Weise können Sie eine Standard-Eingabeaufforderung verwenden oder Ihre eigene Eingabeaufforderung "nichts ausgewählt" als erste Zeile definieren, oder beides, oder keine. (Hinweis: Einige Themen zeigen ein DropDown für einen Spinner anstelle eines Dialogs. Das Dropdown zeigt die Eingabeaufforderung normalerweise nicht an)

Sie definieren ein Layout, das wie eine Eingabeaufforderung aussieht, z. B. ausgegraut...

Initial nothing selected

Verwendung einer Standard-Eingabeaufforderung (beachten Sie, dass nichts ausgewählt ist):

With a standard prompt

Oder mit einem Prompt und etwas Dynamischem (es hätte auch ohne Prompt gehen können):

Prompt and nothing selected row

Verwendung im obigen Beispiel

Spinner spinner = (Spinner) findViewById(R.id.spinner);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.planets_array, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setPrompt("Select your favorite Planet!");

spinner.setAdapter(
      new NothingSelectedSpinnerAdapter(
            adapter,
            R.layout.contact_spinner_row_nothing_selected,
            // R.layout.contact_spinner_nothing_selected_dropdown, // Optional
            this));

kontakt_spinner_reihe_nichts_ausgewaehlt.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    style="?android:attr/spinnerItemStyle"
    android:singleLine="true"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:ellipsize="marquee"
    android:textSize="18sp"
    android:textColor="#808080"
    android:text="[Select a Planet...]" />

NothingSelectedSpinnerAdapter.java

import android.content.Context;
import android.database.DataSetObserver;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListAdapter;
import android.widget.SpinnerAdapter;

/**
 * Decorator Adapter to allow a Spinner to show a 'Nothing Selected...' initially
 * displayed instead of the first choice in the Adapter.
 */
public class NothingSelectedSpinnerAdapter implements SpinnerAdapter, ListAdapter {

    protected static final int EXTRA = 1;
    protected SpinnerAdapter adapter;
    protected Context context;
    protected int nothingSelectedLayout;
    protected int nothingSelectedDropdownLayout;
    protected LayoutInflater layoutInflater;

    /**
     * Use this constructor to have NO 'Select One...' item, instead use
     * the standard prompt or nothing at all.
     * @param spinnerAdapter wrapped Adapter.
     * @param nothingSelectedLayout layout for nothing selected, perhaps
     * you want text grayed out like a prompt...
     * @param context
     */
    public NothingSelectedSpinnerAdapter(
      SpinnerAdapter spinnerAdapter,
      int nothingSelectedLayout, Context context) {

        this(spinnerAdapter, nothingSelectedLayout, -1, context);
    }

    /**
     * Use this constructor to Define your 'Select One...' layout as the first
     * row in the returned choices.
     * If you do this, you probably don't want a prompt on your spinner or it'll
     * have two 'Select' rows.
     * @param spinnerAdapter wrapped Adapter. Should probably return false for isEnabled(0)
     * @param nothingSelectedLayout layout for nothing selected, perhaps you want
     * text grayed out like a prompt...
     * @param nothingSelectedDropdownLayout layout for your 'Select an Item...' in
     * the dropdown.
     * @param context
     */
    public NothingSelectedSpinnerAdapter(SpinnerAdapter spinnerAdapter,
            int nothingSelectedLayout, int nothingSelectedDropdownLayout, Context context) {
        this.adapter = spinnerAdapter;
        this.context = context;
        this.nothingSelectedLayout = nothingSelectedLayout;
        this.nothingSelectedDropdownLayout = nothingSelectedDropdownLayout;
        layoutInflater = LayoutInflater.from(context);
    }

    @Override
    public final View getView(int position, View convertView, ViewGroup parent) {
        // This provides the View for the Selected Item in the Spinner, not
        // the dropdown (unless dropdownView is not set).
        if (position == 0) {
            return getNothingSelectedView(parent);
        }
        return adapter.getView(position - EXTRA, null, parent); // Could re-use
                                                 // the convertView if possible.
    }

    /**
     * View to show in Spinner with Nothing Selected
     * Override this to do something dynamic... e.g. "37 Options Found"
     * @param parent
     * @return
     */
    protected View getNothingSelectedView(ViewGroup parent) {
        return layoutInflater.inflate(nothingSelectedLayout, parent, false);
    }

    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent) {
        // Android BUG! http://code.google.com/p/android/issues/detail?id=17128 -
        // Spinner does not support multiple view types
        if (position == 0) {
            return nothingSelectedDropdownLayout == -1 ?
              new View(context) :
              getNothingSelectedDropdownView(parent);
        }

        // Could re-use the convertView if possible, use setTag...
        return adapter.getDropDownView(position - EXTRA, null, parent);
    }

    /**
     * Override this to do something dynamic... For example, "Pick your favorite
     * of these 37".
     * @param parent
     * @return
     */
    protected View getNothingSelectedDropdownView(ViewGroup parent) {
        return layoutInflater.inflate(nothingSelectedDropdownLayout, parent, false);
    }

    @Override
    public int getCount() {
        int count = adapter.getCount();
        return count == 0 ? 0 : count + EXTRA;
    }

    @Override
    public Object getItem(int position) {
        return position == 0 ? null : adapter.getItem(position - EXTRA);
    }

    @Override
    public int getItemViewType(int position) {
        return 0;
    }

    @Override
    public int getViewTypeCount() {
        return 1;
    }

    @Override
    public long getItemId(int position) {
        return position >= EXTRA ? adapter.getItemId(position - EXTRA) : position - EXTRA;
    }

    @Override
    public boolean hasStableIds() {
        return adapter.hasStableIds();
    }

    @Override
    public boolean isEmpty() {
        return adapter.isEmpty();
    }

    @Override
    public void registerDataSetObserver(DataSetObserver observer) {
        adapter.registerDataSetObserver(observer);
    }

    @Override
    public void unregisterDataSetObserver(DataSetObserver observer) {
        adapter.unregisterDataSetObserver(observer);
    }

    @Override
    public boolean areAllItemsEnabled() {
        return false;
    }

    @Override
    public boolean isEnabled(int position) {
        return position != 0; // Don't allow the 'nothing selected'
                                             // item to be picked.
    }

}

57 Stimmen

Dies ist eine elegante Lösung für das Problem. Code funktioniert genau wie copy'n'pasted in mein Projekt. +1 für keine Reflexion erforderlich.

0 Stimmen

Gibt es dazu ein Demoprojekt, das die Implementierung dieser Klasse zeigt? Ich habe Probleme damit, es zum Laufen zu bringen. Verwenden Sie diese Klasse als Adapter, oder übergeben Sie einen Adapter und verwenden Sie ihn dann?

0 Stimmen

@Chris, Ich habe den Code für die Verwendung im obigen Beispiel hinzugefügt, ich hoffe, das hilft!

265voto

emmby Punkte 98109

Hier ist eine allgemeine Lösung, die sich über die Spinner Ansicht. Sie übersteuert setAdapter() um die Ausgangsposition auf -1 zu setzen, und stellt die mitgelieferte SpinnerAdapter um die Eingabeaufforderung für Positionen kleiner als 0 anzuzeigen.

Diese Lösung wurde unter Android 1.5 bis 4.2 getestet, aber Vorsicht ist geboten! Da diese Lösung auf Reflection basiert, um die private AdapterView.setNextSelectedPositionInt() y AdapterView.setSelectedPositionInt() Es ist nicht garantiert, dass es in zukünftigen Betriebssystem-Updates funktioniert. Es ist wahrscheinlich, dass es funktioniert, aber keineswegs garantiert.

Normalerweise würde ich so etwas nicht dulden, aber diese Frage wurde oft genug gestellt, und es scheint eine vernünftige Anfrage zu sein, so dass ich meine Lösung posten wollte.

/**
 * A modified Spinner that doesn't automatically select the first entry in the list.
 *
 * Shows the prompt if nothing is selected.
 *
 * Limitations: does not display prompt if the entry list is empty.
 */
public class NoDefaultSpinner extends Spinner {

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

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

    public NoDefaultSpinner(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void setAdapter(SpinnerAdapter orig ) {
        final SpinnerAdapter adapter = newProxy(orig);

        super.setAdapter(adapter);

        try {
            final Method m = AdapterView.class.getDeclaredMethod(
                               "setNextSelectedPositionInt",int.class);
            m.setAccessible(true);
            m.invoke(this,-1);

            final Method n = AdapterView.class.getDeclaredMethod(
                               "setSelectedPositionInt",int.class);
            n.setAccessible(true);
            n.invoke(this,-1);
        } 
        catch( Exception e ) {
            throw new RuntimeException(e);
        }
    }

    protected SpinnerAdapter newProxy(SpinnerAdapter obj) {
        return (SpinnerAdapter) java.lang.reflect.Proxy.newProxyInstance(
                obj.getClass().getClassLoader(),
                new Class[]{SpinnerAdapter.class},
                new SpinnerAdapterProxy(obj));
    }

    /**
     * Intercepts getView() to display the prompt if position < 0
     */
    protected class SpinnerAdapterProxy implements InvocationHandler {

        protected SpinnerAdapter obj;
        protected Method getView;

        protected SpinnerAdapterProxy(SpinnerAdapter obj) {
            this.obj = obj;
            try {
                this.getView = SpinnerAdapter.class.getMethod(
                                 "getView",int.class,View.class,ViewGroup.class);
            } 
            catch( Exception e ) {
                throw new RuntimeException(e);
            }
        }

        public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
            try {
                return m.equals(getView) && 
                       (Integer)(args[0])<0 ? 
                         getView((Integer)args[0],(View)args[1],(ViewGroup)args[2]) : 
                         m.invoke(obj, args);
            } 
            catch (InvocationTargetException e) {
                throw e.getTargetException();
            } 
            catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        protected View getView(int position, View convertView, ViewGroup parent) 
          throws IllegalAccessException {

            if( position<0 ) {
                final TextView v = 
                  (TextView) ((LayoutInflater)getContext().getSystemService(
                    Context.LAYOUT_INFLATER_SERVICE)).inflate(
                      android.R.layout.simple_spinner_item,parent,false);
                v.setText(getPrompt());
                return v;
            }
            return obj.getView(position,convertView,parent);
        }
    }
}

0 Stimmen

Ihre Methode ist sehr gut, aber ich habe festgestellt, dass bei der Auswahl eines Elements nicht nur der Text des Elements, sondern auch das Auswahlsymbol auf dem Spinner angezeigt wird.

8 Stimmen

@emmby Haben Sie eine Idee, wie man die Auswahl aufheben kann, nachdem der Benutzer sie festgelegt hat? Ich habe versucht, die beiden invoke()-Aufrufe in eine clearSelection()-Methode umzuwandeln, aber das funktioniert nicht wirklich. Obwohl die Popup-Liste das zuvor ausgewählte Element als nicht ausgewählt anzeigt, zeigt das Spinner-Widget es immer noch als ausgewählt an, und wenn der Benutzer das gleiche Element erneut auswählt, wird onItemSelected() nicht aufgerufen.

5 Stimmen

Könnte jemand erklären, wie man die obige Klasse verwendet?

134voto

HRJ Punkte 16077

Am Ende habe ich eine Button stattdessen. Während eine Button ist kein Spinner kann das Verhalten leicht angepasst werden.

Erstellen Sie zunächst wie gewohnt den Adapter:

String[] items = new String[] {"One", "Two", "Three"};
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
        android.R.layout.simple_spinner_dropdown_item, items);

Beachten Sie, dass ich die simple_spinner_dropdown_item als die Layout-ID. Dies trägt zu einem besseren Aussehen bei der Erstellung des Warndialogs bei.

In der onClick-Handler für meine Schaltfläche habe ich:

public void onClick(View w) {
  new AlertDialog.Builder(this)
  .setTitle("the prompt")
  .setAdapter(adapter, new DialogInterface.OnClickListener() {

    @Override
    public void onClick(DialogInterface dialog, int which) {

      // TODO: user specific action

      dialog.dismiss();
    }
  }).create().show();
}

Und das war's!

10 Stimmen

Ich bin mit dieser Antwort einverstanden. Außerdem ist ein Knopf viel einfacher zu stylen als ein Spinner.

0 Stimmen

@HRJ Ich habe die von Ihnen vorgeschlagene Methode implementiert, aber das zuvor ausgewählte Element wird nicht hervorgehoben (d.h. das Optionsfeld muss mit einem grünen Punkt in der Mitte hervorgehoben werden) .... Wie kann ich dies in der OnClick()-Methode der Schaltfläche zu erreichen. Bitte helfen Sie mir HRJ.....

2 Stimmen

Die Schaltfläche mit diesem Layout ist perfekt <Button Android:id="@+id/city" Android:layout_width="match_parent" Android:layout_height="wrap_content" Android:layout_margin="5dp" Android:gravity="left" Android:background="@Android:drawable/btn_dropdown" Android:text="@string/city_prompt" />

68voto

Casey Punkte 5717

Erstens könnte Sie das folgende Thema interessieren prompt Attribut des Spinner Klasse. Siehe das Bild unten, "Choose a Planet" ist die Aufforderung, die in der XML mit android:prompt="" .

enter image description here

Ich wollte vorschlagen, die Unterklasse Spinner , wo man intern zwei Adapter beibehalten könnte. Einen Adapter, der die Option "Select One" hat, und den anderen real Adapter (mit den aktuellen Optionen), dann mit dem OnClickListener um die Adapter zu wechseln, bevor der Auswahldialog angezeigt wird. Nachdem ich jedoch versucht habe, diese Idee umzusetzen, bin ich zu dem Schluss gekommen, dass man keine OnClick Ereignisse für das Widget selbst.

Sie könnten den Spinner in eine andere Ansicht verpacken, die Klicks auf die Ansicht abfangen und dann Ihre CustomSpinner um den Adapter zu wechseln, aber es scheint ein schrecklicher Hack zu sein.

Müssen Sie wirklich "Select One" anzeigen?

37 Stimmen

Es geht nicht nur darum, dass "Select One" angezeigt werden muss, sondern auch um den Fall, dass der Spinner-Wert optional leer gelassen werden kann.

5 Stimmen

Außerdem wird bei dieser Option die Erde als Auswahl auf dem Spinner angezeigt, bevor etwas ausgewählt wurde. Für meine Anwendung wäre es mir lieber, wenn der Benutzer erkennen könnte, dass er noch nichts ausgewählt hat.

3 Stimmen

Das beantwortet die Frage nicht wirklich. Die Leute suchen nach einer Möglichkeit, den Spinner selbst standardmäßig "Select One" anzeigen zu lassen und nicht das erste Element in der Liste der Planeten, in diesem Beispiel

62voto

Manos Punkte 1411

Dieser Code wurde getestet und funktioniert unter Android 4.4

enter image description here

Spinner spinner = (Spinner) activity.findViewById(R.id.spinner);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(activity, android.R.layout.simple_spinner_dropdown_item) {

            @Override
            public View getView(int position, View convertView, ViewGroup parent) {

                View v = super.getView(position, convertView, parent);
                if (position == getCount()) {
                    ((TextView)v.findViewById(android.R.id.text1)).setText("");
                    ((TextView)v.findViewById(android.R.id.text1)).setHint(getItem(getCount())); //"Hint to be displayed"
                }

                return v;
            }       

            @Override
            public int getCount() {
                return super.getCount()-1; // you dont display last item. It is used as hint.
            }

        };

        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        adapter.add("Daily");
        adapter.add("Two Days");
        adapter.add("Weekly");
        adapter.add("Monthly");
        adapter.add("Three Months");
        adapter.add("HINT_TEXT_HERE"); //This is the text that will be displayed as hint.

        spinner.setAdapter(adapter);
        spinner.setSelection(adapter.getCount()); //set the hint the default selection so it appears on launch.
        spinner.setOnItemSelectedListener(this);

0 Stimmen

getItem(getCount()) ist für mich rot unterstrichen? Kann die Methode setHint nicht auflösen

0 Stimmen

Ich habe einen Zweifel, sah viele Lösungen in diesem Thread..aber warum jeder ist Hinzufügen von Hinweis auf die letzte. Ist es falsch, den Hinweis in der ersten Zeile hinzuzufügen?

0 Stimmen

Ich kann 'setOnItemSelectedListener(this);' nicht setzen, weil ich 'implements NavigationView.OnNavigationItemSelectedListener' verwende, kann ich 'setOnItemSelectedListener(this);' ohne Probleme entfernen?

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X