551 votos

Cómo hacer un Android Spinner con texto inicial "Seleccione Uno"

En Android, quiero usar un Spinner que en un principio (cuando el usuario no ha hecho una selección aún) muestra el texto "Seleccione Una opción". Cuando el usuario hace clic en el control de giro, la lista de artículos que aparece en pantalla y el usuario selecciona una de las opciones. Después de que el usuario ha realizado una selección, el elemento seleccionado se muestra en la Ruleta en lugar de "Seleccionar".

Tengo el siguiente código para crear un control de giro:

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

Con este código, inicialmente el tema "Uno" en la pantalla. Sólo quisiera agregar un nuevo elemento de "Seleccionar" a los elementos, pero luego "Seleccionar Uno" también se muestra en la lista desplegable como primer elemento, que no es lo que quiero.

¿Cómo puedo solucionar este problema?

283voto

aaronvargas Puntos 1881

Lo que usted puede hacer es decorar tu SpinnerAdapter con uno que presenta un 'Seleccione la Opción de... " Ver inicialmente para el control de giro para mostrar con nada seleccionado.

Aquí es un ejemplo de trabajo probado para Android 2.3 y 4.0 (no se utiliza nada en la biblioteca de compatibilidad, así que debería estar bien para un rato), Ya que es un decorador, que deben ser fáciles de adaptar el código existente y funciona bien con CursorLoaders también. (Swap cursor en la envuelta cursorAdapter de curso...)

No es un Android bug que hace que este un poco más difícil a re-utilizar las vistas. (Así que tienes que usar el setTag o alguna otra cosa para asegurarse de que su convertView es correcta.) Spinner no admite varios tipos de vista

Código de notas: 2 constructores

Esto le permite utilizar un estándar de sistema o definir su propia 'nada de lo seleccionado " como la primera fila, o ambos, o ninguno. (Nota: Algunos de los temas mostrar una lista Desplegable para un control de giro en lugar de un cuadro de diálogo. La lista Desplegable normalmente no muestran el símbolo de sistema)

Definir un esquema de 'look' como un símbolo, por ejemplo, en color gris...

Initial nothing selected

El uso de un estándar prompt (aviso que no hay nada seleccionado):

With a standard prompt

O con una pronta y algo dinámico (podría haber tenido ninguna solicitud también):

Prompt and nothing selected row

Uso en el ejemplo anterior

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

contact_spinner_row_nothing_selected.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) {
        // BUG! Vote to fix!! 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) {
        // Doesn't work!! Vote to Fix! http://code.google.com/p/android/issues/detail?id=17128 -
        // Spinner does not support multiple view types
        // This method determines what is the convertView, this should
        // return 1 for pos 0 or return 0 otherwise.
        return position == 0 ?
               getViewTypeCount() - EXTRA :
               adapter.getItemViewType(position - EXTRA);
    }

    @Override
    public int getViewTypeCount() {
        return adapter.getViewTypeCount() + EXTRA;
    }

    @Override
    public long getItemId(int position) {
        return adapter.getItemId(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 ? false : true; // Don't allow the 'nothing selected'
                                             // item to be picked.
    }

}

250voto

emmby Puntos 35359

He aquí una solución general que prevalece sobre el Spinner vista. Reemplaza setAdapter() para establecer la posición inicial a -1, y los servidores proxy de la suministrados SpinnerAdapter para mostrar la cadena de petición de posición a menos de 0.

Esto ha sido probado en Android 1.5 a través de la 4.2, pero el comprador tenga cuidado! Debido a que esta solución se basa en la reflexión para llamar a la privada AdapterView.setNextSelectedPositionint() y AdapterView.setSelectedPositionInt(), esto no está garantizado para funcionar en futuras actualizaciones del sistema operativo. Parece probable que lo hará, pero no es garantizada.

Normalmente no recomendaría algo como esto, pero esta pregunta se ha hecho muchas veces y parece bastante razonable solicitar que pensé que iba a publicar mi solución.

/**
 * 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);
        }
    }
}

129voto

HRJ Puntos 4750

Terminé con un Button lugar. Mientras que un Button no es un Spinner, el comportamiento es fácil de personalizar.

En primer lugar crear el Adaptador como de costumbre:

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

Tenga en cuenta que estoy usando el simple_spinner_dropdown_item como el diseño de id. Esto ayudará a crear un mejor aspecto a la hora de crear el cuadro de diálogo de aviso.

En el onClick para mi Botón que tengo:

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

Y eso es todo!

66voto

Casey Puntos 2020

En primer lugar, usted puede estar interesado en el "prompt" del atributo de la Ruleta de la clase. Ver la foto de abajo, "Elegir un Planeta" es el símbolo que se pueden establecer en el xml con android:prompt=""

Yo iba a sugerir la creación de subclases de Ruleta, donde se podía mantener dos adaptadores internamente. Un adaptador que tiene el "Seleccione Una opción", y el otro real adaptador (con las opciones reales), a continuación, utilizando el OnClickListener para cambiar los adaptadores antes de las elecciones de diálogo se muestra.. sin embargo, después de tratar de implementar esa idea he llegado a la conclusión de que no pueden recibir eventos OnClick para el mismo widget.

Usted podría ajustar el control de giro de un punto de vista diferente, interceptar los clics en la vista y, a continuación, dígale a su CustomSpinner para cambiar el adaptador, pero parece una terrible hack.

¿Usted realmente necesita para mostrar "Seleccionar"?

prompt example

31voto

Encontré esta solución:

String[] items = new String[] {"Select 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);

spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView<?> arg0, View arg1, int position, long id) {
        items[0] = "One";
        selectedItem = items[position];
    }

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

Acaba de cambiar la matriz[0] con "Seleccionar" y, a continuación, en el onItemSelected, cámbiele el nombre a "Uno".

No es una elegante solución, pero funciona :D

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by: