web-dev-qa-db-fra.com

Comment faire un spinner Android avec le texte initial "Select One"

Sous Android, je souhaite utiliser un spinner qui affiche initialement le texte "Select One" (lorsque l'utilisateur n'a pas encore effectué de sélection). Lorsque l'utilisateur clique sur la roulette, la liste des éléments est affichée et l'utilisateur sélectionne l'une des options. Une fois la sélection effectuée par l’utilisateur, l’élément sélectionné est affiché dans le compteur au lieu de "Select One".

J'ai le code suivant pour créer un spinner: 

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

Avec ce code, l'élément "Un" est initialement affiché. Je pourrais simplement ajouter un nouvel élément "Select One" aux éléments, mais "Select One" serait également affiché dans la liste déroulante en tant que premier élément, ce qui n'est pas ce que je veux.

Comment puis-je résoudre ce problème?

512
Pieter Kuijpers

Voici une solution générale qui remplace la vue Spinner. Il remplace setAdapter() pour définir la position initiale sur -1 et envoie un proxy à la SpinnerAdapter fournie pour afficher la chaîne d'invite pour une position inférieure à 0.

Cela a été testé sur Android 1.5 à 4.2, mais méfiez-vous de l'acheteur! Comme cette solution repose sur la réflexion pour appeler les AdapterView.setNextSelectedPositionInt() et AdapterView.setSelectedPositionInt() privés, il ne sera pas garanti que cela fonctionnera dans les futures mises à jour du système d'exploitation. Cela semble probable, mais ce n'est en aucun cas garanti.

Normalement, je ne tolérerais pas quelque chose comme cela, mais cette question a été posée suffisamment de fois et il semble que ce soit une demande suffisamment raisonnable pour que je pense pouvoir afficher ma solution.

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

Ce que vous pouvez faire est de décorer votre SpinnerAdapter avec celle qui présente une vue "Option de sélection ..." au départ pour que le spinner puisse l'afficher avec aucun élément sélectionné.

Voici un exemple de travail testé pour Android 2.3 et 4.0 (il n’utilise rien dans la bibliothèque de compatibilité, cela devrait donc être correct pendant un certain temps). En tant que décorateur, il devrait être facile de mettre à jour le code existant et cela fonctionne également avec CursorLoaders. (Échangez le curseur sur la cursorAdapter enveloppée, bien sûr ...)

Il existe un bogue Android qui rend un peu plus difficile la réutilisation des vues. (Vous devez donc utiliser la variable setTag ou autre chose pour vous assurer que votre convertView est correcte.) Spinner ne prend pas en charge plusieurs types de vues

Notes de code: 2 constructeurs

Cela vous permet d'utiliser une invite standard ou de définir votre propre "élément sélectionné" en tant que première ligne, ou les deux, voire aucun. (Remarque: certains thèmes affichent un menu déroulant pour un compteur au lieu d'une boîte de dialogue. La liste déroulante ne contient normalement pas l'invite.)

Vous définissez une mise en page pour qu'elle ressemble à une invite, par exemple en grisé ...

Initial nothing selected

Utilisation d'une invite standard (notez que rien n'est sélectionné):

With a standard Prompt

Ou avec une invite et quelque chose de dynamique (pourrait également avoir aucune invite):

Prompt and nothing selected row

Utilisation dans l'exemple ci-dessus

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) {
        // 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.
    }

}
273
aaronvargas

J'ai fini par utiliser un Button à la place. Bien qu'une Button ne soit pas une Spinner, le comportement est facile à personnaliser.

Créez d'abord l'adaptateur comme d'habitude:

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

Notez que j’utilise le simple_spinner_dropdown_item comme identifiant de présentation. Cela aidera à créer une meilleure apparence lors de la création du dialogue d'alerte.

Dans le gestionnaire onClick de mon bouton, j'ai:

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

Et c'est tout!

126
HRJ

Je sais que cette question a beaucoup de réponse mais j'ai trouvé le moyen le plus simple et le plus facile à faire.

Cette solution est indépendante du niveau de l'API, elle fonctionnera pour tous les niveaux de l'API.

L’idée est de définir le dernier élément de spinner par défaut. 

spinner.setSelection(lastIndex);//index starts from 0.so if spinner has 5 item the lastIndex is 4

L'élément au dernier index devrait être votre titre de fileur comme "Choisir un pays"

Et tout en remplissant la casserole, diminuez le nombre d’articles de un.

@Override
public int getCount() {
   // don't display last item. It is used as hint.
   int count = super.getCount();
   return count > 0 ? count - 1 : count;
}

Donc, votre flux de code sera comme ça

List<String> objects = new ArrayList<String>();
objects.add("India");
objects.add("Pakistan");
objects.add("China");
// add hint as last item
objects.add("Select Country");

HintAdapter adapter = new HintAdapter(context, objects, Android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(Android.R.layout.simple_spinner_dropdown_item);

Spinner spinnerFilmType = (Spinner) findViewById(R.id.spinner);
spinner.setAdapter(adapter);

// show hint
spinner.setSelection(adapter.getCount());

HintAdapter.Java

public class HintAdapter extends ArrayAdapter<Objects> {

    public HintAdapter(Context theContext, List<Object> objects, int theLayoutResId) {
        super(theContext, theLayoutResId, objects);
    }

    @Override
    public int getCount() {
        // don't display last item. It is used as hint.
        int count = super.getCount();
        return count > 0 ? count - 1 : count;
    }
}

Titre Spinner

 Spinner Title

Élément de fileuse

 Spinner Items

100
Shakeeb Ayaz

Tout d’abord, vous pourriez être intéressé par l’attribut Prompt de la classe Spinner. Voir l'image ci-dessous, "Choisissez une planète" est l'invite qui peut être définie dans le code XML avec Android:Prompt="".

 enter image description here

J'allais suggérer de sous-classer Spinner, où vous pourriez conserver deux adaptateurs en interne. Un adaptateur doté de l'option "Sélectionner un" et l'autre adaptateur real (avec les options réelles), puis utilisez la variable OnClickListener pour changer de carte avant l'affichage de la boîte de dialogue de choix. Cependant, après avoir essayé de mettre en œuvre cette idée, je suis parvenu à la conclusion que vous ne pouvez pas recevoir d'événements OnClick pour le widget lui-même.

Vous pouvez envelopper la visionneuse dans une vue différente, intercepter les clics sur la vue, puis demander à votre CustomSpinner de changer d'adaptateur, mais cela semble être un terrible piratage.

Avez-vous vraiment besoin de montrer "Select One"? 

66
Casey

Ce code a été testé et fonctionne sur 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);
53
Manos

J'ai trouvé cette solution:

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

Il suffit de changer le tableau [0] avec "Select One", puis dans le onItemSelected, renommez-le en "One".

Pas une solution élégante, mais ça marche: D

30
Marco da Gualdo

Il n'y a pas d'API par défaut pour définir un indice sur Spinner. Pour l'ajouter, nous avons besoin d'une petite solution de contournement sans mettre en œuvre une réflexion de sécurité

List<Object> objects = new ArrayList<Object>();
objects.add(firstItem);
objects.add(secondItem);
// add hint as last item
objects.add(hint);

HintAdapter adapter = new HintAdapter(context, objects, Android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(Android.R.layout.simple_spinner_dropdown_item);

Spinner spinnerFilmType = (Spinner) findViewById(R.id.spinner);
spinner.setAdapter(adapter);

// show hint
spinner.setSelection(adapter.getCount());

Source de l'adaptateur: 

public class HintAdapter
        extends ArrayAdapter<Objects> {

    public HintAdapter(Context theContext, List<Object> objects) {
        super(theContext, Android.R.id.text1, Android.R.id.text1, objects);
    }

    public HintAdapter(Context theContext, List<Object> objects, int theLayoutResId) {
        super(theContext, theLayoutResId, Android.R.id.text1, objects);
    }

    @Override
    public int getCount() {
        // don't display last item. It is used as hint.
        int count = super.getCount();
        return count > 0 ? count - 1 : count;
    }
}

Source primaire

19
Yakiv Mospan

Beaucoup de réponses ici, mais je suis surpris que personne n'ait suggéré une solution simple: placez un TextView sur le Spinner. Définissez un écouteur de clic sur le TextView qui masque le TextView, affiche le Spinner et appelle spinner.performClick ().

13
mjancola

J'ai le même problème pour spinner, avec une sélection vide, et j'ai trouvé une meilleure solution. Regardez ce code simple.

Spinner lCreditOrDebit = (Spinner)lCardPayView.findViewById(R.id.CARD_TYPE);
spinneradapter lAdapter = 
  new spinneradapter(
    BillPayScreen.this, 
    ndroid.R.layout.simple_spinner_item,getResources().getStringArray(R.array.creditordebit));
lAdapter.setDropDownViewResource(Android.R.layout.simple_spinner_dropdown_item);
lCreditOrDebit.setAdapter(lAdapter);

Ici spinneradapter est une petite personnalisation pour arrayadapter. Cela ressemble à ceci:

import Android.content.Context;
import Android.view.View;
import Android.view.ViewGroup;
import Android.widget.ArrayAdapter;
import Android.widget.ImageView;

public class spinneradapter extends ArrayAdapter<String>{
    private Context m_cContext;
    public spinneradapter(Context context,int textViewResourceId, String[] objects) {
        super(context, textViewResourceId, objects);
        this.m_cContext = context;
    }

    boolean firsttime = true;
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if(firsttime){
            firsttime = false;
            //Just return some empty view
            return new ImageView(m_cContext);
        }
        //Let the array adapter take care of it this time.
        return super.getView(position, convertView, parent);
    }
}
9
Rajasekhar

Fichier XML: 

<Spinner Android:id="@+id/locationSpinner"
    Android:layout_width="fill_parent"
    Android:layout_height="wrap_content"
    Android:Prompt="@string/select_location" />

Activité:

private Spinner featuresSelection;
private ArrayAdapter<CharSequence> featuresAdapter;
private List<CharSequence> featuresList;

onCreate:

featuresList = new ArrayList<CharSequence>();
featuresAdapter = new ArrayAdapter<CharSequence>(this,
  Android.R.layout.simple_spinner_item, featuresList);
featuresAdapter.setDropDownViewResource(
  Android.R.layout.simple_spinner_dropdown_item);
featuresSelection = ((Spinner) yourActivity.this
  .findViewById(R.id.locationSpinner));
featuresSelection.setAdapter(featuresAdapter);
featuresSelection.setOnItemSelectedListener(
  new MyOnItemSelectedListener());

Certaines fonctions (ajouter des éléments à l'adaptateur par programme)>

featuresAdapter.add("some string");

Vous avez maintenant un disque vide et vous pouvez écrire du code pour ne pas ouvrir la boîte de dialogue si elle est vide. Ou ils peuvent appuyer en arrière. Mais vous le remplissez également avec une fonction ou une autre liste pendant l'exécution.

6
trgraglia

Vous pouvez le changer en une vue texte et utiliser ceci:

Android:style="@Android:style/Widget.DeviceDefault.Light.Spinner"

puis définissez la propriété Android:text.

6
Christian Vielma

J'ai essayé comme le suivant. Prenez un bouton et donnez l'événement click. En changeant l'arrière-plan du bouton, il semble que ce soit un spinner.

Déclarer comme variables globales alertdialog et valeur par défaut.

AlertDialog d;
static int default_value = 0;
final Button btn = (Button) findViewById(R.id.button1);
btn .setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v)
{
    //c.show();
    final CharSequence str[] = {"Android","Black Berry","Iphone"};
        AlertDialog.Builder builder =
          new AlertDialog.Builder(TestGalleryActivity.this).setSingleChoiceItems(
            str, default_value,new  DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int position)
            {
                Toast.makeText(TestGalleryActivity.this,
                               "" + position,
                               Toast.LENGTH_SHORT).show();
                default_value = position;
                btn.setText(str[position]);
                if(d.isShowing())
                    d.dismiss();
            }
        }).setTitle("Select Any");
        d = builder.create();
        d.show();
    }
});
4
Ramesh Akula

J'ai un spinner sur mon main.xml et son identifiant est @+id/spinner1

voici ce que j'écris dans ma fonction OnCreate:

spinner1 = (Spinner)this.findViewById(R.id.spinner1);
final String[] groupes = new String[] {"A", "B", "C", "D", "E", "F", "G", "H"};
ArrayAdapter<CharSequence> featuresAdapter = new ArrayAdapter<CharSequence>(this, Android.R.layout.simple_spinner_item, new ArrayList<CharSequence>());
featuresAdapter.setDropDownViewResource(Android.R.layout.simple_spinner_dropdown_item);
spinner1.setAdapter(featuresAdapter);
for (String s : groupes) featuresAdapter.add(s);

spinner1.setOnItemSelectedListener(new OnItemSelectedListener() {
     public void onItemSelected(AdapterView<?> arg0, View arg1, int position, long id) {
         // Here go your instructions when the user chose something
         Toast.makeText(getBaseContext(), groupes[position], 0).show();
     }
     public void onNothingSelected(AdapterView<?> arg0) { }
});

Il n'a besoin d'aucune implémentation dans la classe.

3
Cyril

Jetez un coup d'œil à l'application iosched pour trouver une solution générique permettant d'ajouter un élément en haut de la liste. En particulier, si vous utilisez un CursorAdapter, regardez TracksAdapter.Java qui étend cette définition pour fournir une méthode "setHasAllItem" et le code associé permettant de gérer le nombre de listes pour traiter l'élément supplémentaire en haut.

À l’aide de l’adaptateur personnalisé, vous pouvez définir le texte sur "Séléctionner une" ou autre chose que vous voudriez que l’élément supérieur dise.

3
Sport

C'est mon chemin:

List<String> list = new ArrayList<String>();
list.add("string1");
list.add("string2");
list.add("string3");
list.add("[Select one]");
final int listsize = list.size() - 1;
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this,Android.R.layout.simple_spinner_item, list) {
 @Override
public int getCount() {
    return(listsize); // Truncate the list
}
};
dataAdapter.setDropDownViewResource(Android.R.layout.simple_spinner_dropdown_item);
mySpinner.setAdapter(dataAdapter);

mySpinner.setSelection(listsize); // Hidden item to appear in the spinner
3
Cabezas

Je pense que le moyen le plus simple est de créer un élément factice sur l'index 0 en disant "sélectionnez-en un", puis en sauvegardant, vérifiez que la sélection n'est pas 0.

2
Tobias

J'ai trouvé beaucoup de bonnes solutions pour cela. la plupart fonctionne en ajoutant un élément à la fin de l'adaptateur, et n'affiche pas le dernier élément de la liste déroulante . . Ainsi, l'utilisateur voit les derniers éléments au lieu des premiers (dans le cas où il y a beaucoup d'éléments à afficher), après avoir touché le bouton fléché pour la première fois.

Donc, je mets l'élément indice au début de la liste. et masquer le premier élément de la liste déroulante.

private void loadSpinner(){

    HintArrayAdapter hintAdapter = new HintArrayAdapter<String>(context, 0);

    hintAdapter.add("Hint to be displayed");
    hintAdapter.add("Item 1");
    hintAdapter.add("Item 2");
            .
            .
    hintAdapter.add("Item 30");

    spinner1.setAdapter(hintAdapter);

    //spinner1.setSelection(0); //display hint. Actually you can ignore it, because the default is already 0
    //spinner1.setSelection(0, false); //use this if don't want to onItemClick called for the hint

    spinner1.setOnItemSelectedListener(yourListener);
}

private class HintArrayAdapter<T> extends ArrayAdapter<T> {

    Context mContext;

    public HintArrayAdapter(Context context, int resource) {
        super(context, resource);
        this.mContext = context
    }

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

        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        View view = inflater.inflate(Android.R.layout.simple_spinner_item, parent, false);
        TextView texview = (TextView) view.findViewById(Android.R.id.text1);

        if(position == 0) {
            texview.setText("");
            texview.setHint(getItem(position).toString()); //"Hint to be displayed"
        } else {
            texview.setText(getItem(position).toString());
        }

        return view;
    }

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

        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        View view;

        if(position == 0){
            view = inflater.inflate(R.layout.spinner_hint_list_item_layout, parent, false); // Hide first row
        } else {
            view = inflater.inflate(Android.R.layout.simple_spinner_dropdown_item, parent, false);
            TextView texview = (TextView) view.findViewById(Android.R.id.text1);
            texview.setText(getItem(position).toString());
        } 

        return view;
    }
}

définissez la disposition ci-dessous dans @Override getDropDownView () lorsque la position est 0, pour masquer la première ligne de conseil.

R.layout.spinner_hint_list_item_layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content" >

</LinearLayout>
2
Alireza Sobhani

Donc, ceci est mon dernier exemple "all-in" pour un bouton-spinner

Dans activity_my_form.xml

    <Button
        Android:id="@+id/btnSpinnerPlanets"
        Android:layout_width="fill_parent"
        Android:layout_height="wrap_content"
        Android:gravity="left|center_vertical"
        Android:singleLine="true"
        Android:text="@string/selectAPlanet"
        Android:textSize="10sp"
        Android:background="@Android:drawable/btn_dropdown">
    </Button>

Dans strings.xml

<string name="selectAPlanet">Select planet&#8230;</string>

<string-array name="planets__entries">
    <item>The Sun with a name very long so long long long long longThe Sun with a name very long so long long long long longThe Sun with a name very long so long long long long long</item>
    <item>Mercury</item>
    <item>Venus</item>
    <item>Earth</item>
    <item>Mars</item>
    <item>Jupiter</item>
    <item>Saturn</item>
    <item>Uranus</item>
    <item>Neptune</item>
</string-array>

Dans MyFormActivity.Java

public class MyFormActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        ((Button) findViewById(R.id.btnSpinnerPlanets)).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                final String[] items = view.getResources().getStringArray(R.array.planets__entries);
                ArrayAdapter<String> adapter = new ArrayAdapter<String>(MyFormActivity.this, Android.R.layout.simple_spinner_dropdown_item, items);
                new AlertDialog.Builder(MyFormActivity.this).setTitle("the Prompt").setAdapter(adapter, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        ((Button) findViewById(R.id.btnSpinnerPlanets)).setText(items[which]);
                        dialog.dismiss();
                    }
                }).create().show();
            }
        });     

    }

}   

Enfin, j’ai obtenu une taille de police configurable sans premier élément sélectionnable. Bouton spinner !!! Merci à HRJ

2
wildnove

Lorsque vous étendez SpinnerAdapter, vous substituez deux méthodes View-, getView(int, View, ViewGroup) et getDropDownView(int, View, ViewGroup). Le premier fournit le View inséré dans le Spinner lui-même; le second fournit le View dans la liste déroulante (comme son nom l’indique). Vous pouvez remplacer la getView(...) de sorte que, tant qu’un élément n’a pas été sélectionné, il affiche un TextView contenant une invite; puis, lorsque vous détectez qu'un élément a été sélectionné, vous le changez pour afficher un TextView correspondant à celui-ci.

public class PromptingAdapter extends SpinnerAdapter {

    //... various code ...

    private boolean selectionmade = false;

    //call this method from the OnItemSelectedListener for your Spinner
    public setSelectionState(boolean b) {
        selectionmade = b;
    }

    @Override
    public View getView(int position, View recycle, ViewGroup container) {
        if(selectionmade) {
            //your existing code to supply a View for the Spinner
            //you could even put "return getDropDownView(position, recycle, container);"
        }
        else {
            View output;
            if(recycle instanceof TextView) {
                 output = recycle;
            }
            else {
                 output = new TextView();
                 //and layout stuff
            }
            output.setText(R.string.please_select_one);
            //put a string "please_select_one" in res/values/strings.xml
            return output;
        }
    }

//...
}
1
Andrew Wyld

Pour ceux qui utilisent Xamarin, voici l'équivalent en C # de la réponse ci-dessus d'Aaronvargas.

using Android.Content;
using Android.Database;
using Android.Views;
using Android.Widget;
using Java.Lang;

namespace MyNamespace.Droid
{ 
  public class NothingSelectedSpinnerAdapter : BaseAdapter, ISpinnerAdapter, IListAdapter
  {
    protected static readonly int EXTRA = 1;
    protected ISpinnerAdapter adapter;
    protected Context context;
    protected int nothingSelectedLayout;
    protected int nothingSelectedDropdownLayout;
    protected LayoutInflater layoutInflater;

    public NothingSelectedSpinnerAdapter(ISpinnerAdapter spinnerAdapter, int nothingSelectedLayout, Context context) : this(spinnerAdapter, nothingSelectedLayout, -1, context)
    {
    }

    public NothingSelectedSpinnerAdapter(ISpinnerAdapter spinnerAdapter, int nothingSelectedLayout, int nothingSelectedDropdownLayout, Context context)
    {
      this.adapter = spinnerAdapter;
      this.context = context;
      this.nothingSelectedLayout = nothingSelectedLayout;
      this.nothingSelectedDropdownLayout = nothingSelectedDropdownLayout;
      layoutInflater = LayoutInflater.From(context);
    }

    protected View GetNothingSelectedView(ViewGroup parent)
    {
      return layoutInflater.Inflate(nothingSelectedLayout, parent, false);
    }

    protected View GetNothingSelectedDropdownView(ViewGroup parent)
    {
      return layoutInflater.Inflate(nothingSelectedDropdownLayout, parent, false);
    }

    public override Object GetItem(int position)
    {
      return position == 0 ? null : adapter.GetItem(position - EXTRA);
    }

    public override long GetItemId(int position)
    {
      return position >= EXTRA ? adapter.GetItemId(position - EXTRA) : position - EXTRA;
    }

    public override 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);
      }

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

    public override int Count
    {
      get
      {
        int count = this.adapter.Count;
        return count == 0 ? 0 : count + EXTRA;
      }
    }

    public override 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);
    }

    public override int GetItemViewType(int position)
    {
      return 0;
    }

    public override int ViewTypeCount => 1;

    public override bool HasStableIds => this.adapter.HasStableIds;

    public override bool IsEmpty => this.adapter.IsEmpty;

    public override void RegisterDataSetObserver(DataSetObserver observer)
    {
      adapter.RegisterDataSetObserver(observer);
    }

    public override void UnregisterDataSetObserver(DataSetObserver observer)
    {
      adapter.UnregisterDataSetObserver(observer);
    }

    public override bool AreAllItemsEnabled()
    {
      return false;
    }

    public override bool IsEnabled(int position)
    {
      return position > 0;
    }
  }
}
1
MPavlak

J'ai également résolu ce problème en utilisant le code suivant. Supposons que vous rencontriez une liste d’éléments, par exemple. 

ArrayList<Item> itemsArrayList = new ArrayList<Item>();
Item item1 = new Item();
item1.setId(1);
item1.setData("First Element");
Item item2 = new Item();
item2.setId(2);
Item2.setData("Second Element");
itemsArrayList.add(item1);
itemsArrayList.add(item2);

Maintenant, nous devons fournir les chaînes à spinner car spinner ne peut pas comprendre l'objet. Nous allons donc créer une nouvelle liste de tableaux avec des éléments de chaîne comme ceci ->

ArrayList<String> itemStringArrayList = new ArrayList<String>();
for(Item item : itemsArrayList) {
    itemStringArrayList.add(item.getData());
}

Nous avons maintenant une liste de tableaux itemStringArrayList avec deux éléments de chaîne. Et nous devons afficher le texte "Select Item" comme premier élément. Nous devons donc insérer une nouvelle chaîne dans la variable itemStringArrayList.

itemStringArrayList.add("Select Item");

Nous avons maintenant une liste de tableaux itemsArrayList et nous voulons afficher deux éléments dans la liste déroulante. Mais la condition est la suivante ... Si nous ne sélectionnons rien, alors Select Item devrait apparaître en tant que premier élément qui ne sera pas activé.

Nous pouvons donc implémenter cette fonctionnalité comme ceci. Si vous devez charger les éléments de la liste de tableaux dans Android spinner. Vous devrez donc utiliser un adaptateur. Donc, ici, je vais utiliser la variable ArrayAdapter. Nous pouvons également utiliser l'adaptateur de personnalisation. 

ArrayAdapter<String> itemsArrayAdapter = new ArrayAdapter<String>(getApplicationContext(), R.layout.spinner_item, itemsArrayList){
        @Override
        public boolean isEnabled(int position) {
            if(position == 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        @Override
        public View getDropDownView(int position, View convertView,
                                    ViewGroup parent) {
            View view = super.getDropDownView(position, convertView, parent);
            TextView tv = (TextView) view;
            if(position == 0){
                // Set the hint text color gray
                tv.setTextColor(Color.GRAY);
            }
            else {
                tv.setTextColor(Color.BLACK);
            }
            return view;
        }
    };

itemsArrayAdapter.setDropDownViewResource(R.layout.spinner_item);
your_spinner_name.setAdapter(itemsArrayAdapter);

Ici dans ce code. nous utilisons la disposition spinner personnalisée, c.-à-d. R.layout.spinner_item. C'est une simple vue texte 

<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:padding="10dp"
    Android:textStyle="italic"
    Android:fontFamily="sans-serif-medium"
    />

Nous devons désactiver le premier texte du cinéaste. Donc, pour la position 0, nous désactivons le texte. Et la couleur aussi, nous pouvons définir en écrasant la méthode getDropDownView. Donc, de cette façon, nous aurons le spinner attendu. 

1
Shravan Jain

La meilleure solution que j'ai trouvée pour cela consiste à ne pas utiliser de Spinner mais d'AutoCompleteTextView. C'est en gros un EditText avec Spinner attaché pour montrer les suggestions que vous tapez - mais, avec la bonne configuration, il peut se comporter exactement comme les souhaits de l'OP et plus encore.

XML:

<com.google.Android.material.textfield.TextInputLayout
                Android:id="@+id/item"
                Android:layout_width="match_parent"
                Android:layout_height="wrap_content">

            <androidx.appcompat.widget.AppCompatAutoCompleteTextView
                    Android:id="@+id/input"
                    Android:hint="Select one"
                    style="@style/AutoCompleteTextViewDropDown"/>
        </com.google.Android.material.textfield.TextInputLayout>

Style:

<style name="AutoCompleteTextViewDropDown">
    <item name="Android:clickable">false</item>
    <item name="Android:cursorVisible">false</item>
    <item name="Android:focusable">false</item>
    <item name="Android:focusableInTouchMode">false</item>
    <item name="Android:layout_width">match_parent</item>
    <item name="Android:layout_height">wrap_content</item>
</style>

En ce qui concerne l'adaptateur, utilisez l'adaptateur ArrayAdapter de base ou étendez-le pour créer le vôtre, mais aucune personnalisation supplémentaire du côté de l'adaptateur n'est nécessaire. Définissez l'adaptateur sur AutoCompleteTextView.

0
Chapz

pour que ce soit facile comme ça:

String[] listAges = getResources().getStringArray(R.array.ages);

        // Creating adapter for spinner
        ArrayAdapter<String> dataAdapter =
                new ArrayAdapter<String>(this, Android.R.layout.simple_spinner_item, listAges);

        // Drop down layout style - list view with radio button
        dataAdapter.setDropDownViewResource(Android.R.layout.simple_spinner_dropdown_item);

        // attaching data adapter to spinner
        spinner_age.getBackground().setColorFilter(ContextCompat.getColor(this, R.color.spinner_icon), PorterDuff.Mode.SRC_ATOP);
        spinner_age.setAdapter(dataAdapter);
        spinner_age.setSelection(0);
        spinner_age.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                String item = parent.getItemAtPosition(position).toString();

                if(position > 0){
                    // get spinner value
                    Toast.makeText(parent.getContext(), "Age..." + item, Toast.LENGTH_SHORT).show();
                }else{
                    // show toast select gender
                    Toast.makeText(parent.getContext(), "none" + item, Toast.LENGTH_SHORT).show();
                }
            }
            @Override
            public void onNothingSelected(AdapterView<?> parent) {
            }
        });
0
MrG
public AdapterView.OnItemSelectedListener instructorSpinnerListener = new AdapterView.OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView << ? > adapterView, View view, int i, long l) {
        String selectedInstructorName = adapterView.getItemAtPosition(i).toString();
        if (selectedInstructorName.equals("[Select Instructor]")) {
            instructorSpinnerAdapter.clear();
            for (Offering offering: allOfferingsList)
                instructorSpinnerAdapter.add(offering);
        } else {
            instructorSpinnerAdapter.clear();
        }
    }

    @Override
    public void onNothingSelected(AdapterView<< ? > adapterView) {
        adapterView.setSelection(0);

        // Toast.makeText(getApplicationContext(), "Why?", Toast.LENGTH_SHORT).show();
    }
};
0
Leace

J'utiliserais simplement un RadioGroup with Radio Buttons si vous n'avez que trois choix, vous pouvez les désactiver tous au début.

0
stephane k.

De plus, il existe une astuce simple pour afficher les valeurs par défaut:

Vous pouvez ajouter une valeur par défaut à votre liste, puis ajouter toute votre collection à l'aide de list.addAll(yourCollection);.

Exemple de code exploitable ici:

List<FuelName> fuelList = new ArrayList<FuelName>();
                    fuelList.add(new FuelName(0,"Select One"));
                    fuelList.addAll(response.body());
                    ArrayAdapter adapter = new ArrayAdapter<>(getActivity(), Android.R.layout.simple_spinner_item, fuelList);
                    //fuelName.setPrompt("Select Fuel");
                    fuelName.setAdapter(adapter);

J'espère que ça va récupérer votre complexité. Bonne codage!

0
Majedur Rahaman

pour moi cela a fonctionné quelque chose comme ça. a l'amélioration qui ne modifie que le texte dans CERTAINES options, pas du tout.

D'abord, je prends les noms du disque et crée le tableau avec une vue de personnalisation, mais peu importe maintenant, la clé remplace le getView et, à l'intérieur, change les valeurs à modifier. Dans mon cas, c’était seulement le premier, le reste je laisse l’original

public void rellenarSpinnerCompeticiones(){
        spinnerArrayCompeticiones = new ArrayList<String>();
        for(Competicion c: ((Controlador)getApplication()).getCompeticiones()){
            spinnerArrayCompeticiones.add(c.getNombre());
        }
        //ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(this,R.layout.spinner_item_competicion,spinnerArrayCompeticiones);
        ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(this, R.layout.spinner_item_competicion, spinnerArrayCompeticiones){
            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                final View v = vi.inflate(R.layout.spinner_item_competicion, null);
                final TextView t = (TextView)v.findViewById(R.id.tvCompeticion);
                if(spinnerCompeticion.getSelectedItemPosition()>0){
                    t.setText(spinnerArrayCompeticiones.get(spinnerCompeticion.getSelectedItemPosition()));
                }else{
                    t.setText("Competiciones");
                }
                return v;
            }
        };
        spinnerArrayAdapter.setDropDownViewResource(Android.R.layout.simple_spinner_dropdown_item);
        spinnerCompeticion.setAdapter(spinnerArrayAdapter);
    }
0
Rako

voici un simple

    private boolean isFirst = true;
private void setAdapter() {
    final ArrayList<String> spinnerArray = new ArrayList<String>();     
    spinnerArray.add("Select your option");
    spinnerArray.add("Option 1");
    spinnerArray.add("Option 2");
    spin.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
            TextView tv = (TextView)selectedItemView;
            String res = tv.getText().toString().trim();
            if (res.equals("Option 1")) {
            //do Something
        } else if (res.equals("Option 2")) {
            //do Something else
        }
        }

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

    });

    ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.my_spinner_style,spinnerArray) {
         public View getView(int position, View convertView, ViewGroup parent) {
             View v = super.getView(position, convertView, parent);
             int height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 25, getResources().getDisplayMetrics());                  
             ((TextView) v).setTypeface(tf2);
             ((TextView) v).getLayoutParams().height = height;
             ((TextView) v).setGravity(Gravity.CENTER);
             ((TextView) v).setTextSize(TypedValue.COMPLEX_UNIT_SP, 19);
             ((TextView) v).setTextColor(Color.WHITE);
             return v;
         }

         public View getDropDownView(int position, View convertView,
                 ViewGroup parent) {
             if (isFirst) {
                 isFirst = false;
                 spinnerArray.remove(0);
             }
             View v = super.getDropDownView(position, convertView, parent);                  
             ((TextView) v).setTextColor(Color.argb(255, 70, 70, 70));
             ((TextView) v).setTypeface(tf2);
             ((TextView) v).setGravity(Gravity.CENTER);
             return v;
         }
     };
     spin.setAdapter(adapter);
}
0
Sayka

Reportez-vous à l'une des réponses ci-dessus: https://stackoverflow.com/a/23005376/1312796

J'ai ajouté mon code pour corriger un petit bug. C'est là où aucune donnée n'a été récupérée. Comment afficher le texte d'invite? 

Voici mon tour ... Cela fonctionne bien avec moi. ! 

Essayez de mettre votre disque dans un tableau relatif et alignez un Textview avec votre disque et jouez avec la visibilité de Textview (SHOW/HIDE) à chaque fois que l’adaptateur du disque est chargé ou vide. 

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:layout_marginLeft="10dp"
Android:layout_marginRight="10dp"
Android:layout_marginTop="20dp"
Android:background="#ededed"
Android:orientation="vertical">



    <TextView
        Android:id="@+id/txt_Prompt_from"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_centerVertical="true"
        Android:textColor="@color/gray"
        Android:textSize="16sp"
        Android:layout_alignStart="@+id/sp_from"
        Android:text="From"
        Android:visibility="gone"/>

    <Spinner
        Android:id="@+id/sp_from"
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:layout_centerVertical="true"
        />

Voici le code:

  txt__from = (TextView) rootView.findViewById(R.id.txt_Prompt_from);

appelez cette méthode après et avant l'adaptateur spinner chargé et vide. 

setPromptTextViewVisibility (); //True or fales 

public void setPromptTextViewVisibility (boolean visible )
{
    if (visible)
    {
        txt_from.setVisibility(View.VISIBLE);
    }
    else
    {
        txt_from.setVisibility(View.INVISIBLE);
    }

}
0
Ibrahim AbdelGawad

Hier, je faisais face au même problème et je ne voulais pas ajouter d’élément caché à ArrayAdapter ni utiliser les réflexions, ce qui fonctionne bien mais est un peu sale.

Après avoir lu de nombreux articles et essayé plusieurs solutions, j'ai trouvé une solution en étendant ArrayAdapter et en remplaçant la méthode getView.

import Android.content.Context;
import Android.support.annotation.NonNull;
import Android.view.LayoutInflater;
import Android.view.View;
import Android.view.ViewGroup;
import Android.widget.ArrayAdapter;
import Android.widget.Spinner;
import Android.widget.TextView;

/**
 * A SpinnerAdapter which does not show the value of the initial selection initially,
 * but an initialText.
 * To use the spinner with initial selection instead call notifyDataSetChanged().
 */
public class SpinnerAdapterWithInitialText<T> extends ArrayAdapter<T> {

    private Context context;
    private int resource;

    private boolean initialTextWasShown = false;
    private String initialText = "Please select";

    /**
     * Constructor
     *
     * @param context The current context.
     * @param resource The resource ID for a layout file containing a TextView to use when
     *                 instantiating views.
     * @param objects The objects to represent in the ListView.
     */
    public SpinnerAdapterWithInitialText(@NonNull Context context, int resource, @NonNull T[] objects) {
        super(context, resource, objects);
        this.context = context;
        this.resource = resource;
    }

    /**
     * Returns whether the user has selected a spinner item, or if still the initial text is shown.
     * @param spinner The spinner the SpinnerAdapterWithInitialText is assigned to.
     * @return true if the user has selected a spinner item, false if not.
     */
    public boolean selectionMade(Spinner spinner) {
        return !((TextView)spinner.getSelectedView()).getText().toString().equals(initialText);
    }

    /**
     * Returns a TextView with the initialText the first time getView is called.
     * So the Spinner has an initialText which does not represent the selected item.
     * To use the spinner with initial selection instead call notifyDataSetChanged(),
     * after assigning the SpinnerAdapterWithInitialText.
     */
    @Override
    public View getView(int position, View recycle, ViewGroup container) {
        if(initialTextWasShown) {
            return super.getView(position, recycle, container);
        } else {
            initialTextWasShown = true;
            LayoutInflater inflater = LayoutInflater.from(context);
            final View view = inflater.inflate(resource, container, false);

            ((TextView) view).setText(initialText);

            return view;
        }
    }
}

Ce que fait Android lors de l’initialisation de Spinner, appelle getView pour l’élément sélectionné avant d’appeler getView pour tous les éléments du T[] objects. La SpinnerAdapterWithInitialText renvoie une TextView avec la initialText, la première fois qu’il est appelé . il appelle super.getView qui est la méthode getView de ArrayAdapter qui est appelée si vous utilisez normalement Spinner.

Pour savoir si l'utilisateur a sélectionné un élément de spinner ou si le spinner affiche toujours la variable initialText, appelez selectionMade et remettez-lui le spinner auquel l'adaptateur est affecté.

0
pfaehlfd

Semble une solution banale, mais je mets généralement simplement un TextView à l’avant de la roulette. L'ensemble du XML ressemble à ceci. (hé les gars, ne me tirez pas dessus, je sais que certains d'entre vous n'aiment pas ce genre de mariage):

<FrameLayout
    Android:id="@+id/selectTypesLinear"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:orientation="horizontal" >

    <Spinner
        Android:id="@+id/spinnerExercises"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:entries="@array/exercise_spinner_entries"
        Android:Prompt="@string/exercise_spinner_Prompt"
     />                         
    <TextView
        Android:id="@+id/spinnerSelectText"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:text="Hey! Select this guy!"
        Android:gravity="center"
        Android:background="#FF000000" />


</FrameLayout>

Ensuite, je cache le TextView lorsqu'un élément a été sélectionné. De toute évidence, la couleur d'arrière-plan de TextView doit être identique à celle de Spinner. Fonctionne sur Android 4.0. Je ne sais pas sur les anciennes versions.

Oui. Parce que Spinner appelle setOnItemSelectedListener au début, masquer la vue texte peut être un peu délicat, mais peut être fait de cette façon:

    Boolean controlTouched;

    exerciseSpinner.setOnTouchListener(new OnTouchListener() {


        @Override
        public boolean onTouch(View v, MotionEvent event) {
            controlTouched = true; // I touched it but but not yet selected an Item.
            return false;
        }

    });
    exerciseSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {

        @Override
        public void onItemSelected(AdapterView<?> arg0, View arg1,
                int arg2, long arg3) {
            if (controlTouched) { // Are you sure that I touched it with my fingers and not someone else  ?
                spinnerSelText.setVisibility(View.GONE);
            }
        }

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

    });
0
Claudio Ferraro

Je gère cela en utilisant un bouton au lieu d'un Spinner. J'ai l'exemple de projet sur GitHub.

Dans le projet, j'affiche à la fois le spinner et le bouton pour montrer qu'ils ont effectivement l'air identiques. Sauf le bouton, vous pouvez définir le texte initial comme bon vous semble. 

Voici à quoi ressemble l'activité:

package com.stevebergamini.spinnerbutton;

import Android.app.Activity;
import Android.app.AlertDialog;
import Android.content.DialogInterface;
import Android.os.Bundle;
import Android.view.View;
import Android.view.View.OnClickListener;
import Android.widget.Button;
import Android.widget.Spinner;

public class MainActivity extends Activity {

    Spinner spinner1;
    Button button1;
    AlertDialog ad;
    String[] countries;

    int selected = -1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        spinner1 = (Spinner) findViewById(R.id.spinner1);
        button1 = (Button) findViewById(R.id.button1);

        countries = getResources().getStringArray(R.array.country_names);

        //  You can also use an adapter for the allert dialog if you'd like
        //  ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, Android.R.layout.simple_spinner_dropdown_item, countries);        

        ad = new AlertDialog.Builder(MainActivity.this).setSingleChoiceItems(countries, selected,  
                new  DialogInterface.OnClickListener() {

                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            button1.setText(countries[which]);
                            selected = which;
                            ad.dismiss();

                        }}).setTitle(R.string.select_country).create(); 


        button1.setOnClickListener( new OnClickListener(){

            @Override
            public void onClick(View v) {
                ad.getListView().setSelection(selected);
                ad.show();              
            }});

    }

}

NOTE: Oui, je réalise que cela dépend du thème appliqué et son apparence sera légèrement différente si vous utilisez Theme.Holo. Toutefois, si vous utilisez l'un des thèmes hérités tels que Theme.Black, vous pouvez continuer.

0
SBerg413

Aucune des réponses précédemment soumises ne fonctionnait vraiment comme je voulais résoudre ce problème. Pour moi, la solution idéale fournirait le “Select One” (ou le texte initial) lorsque le compteur est affiché. Lorsque l'utilisateur appuie sur la roulette, le texte initial ne doit pas faire partie du menu déroulant affiché.

Pour compliquer davantage ma situation particulière, mes données spinner proviennent d'un curseur chargé via les rappels de LoaderManager.

Après une expérimentation considérable, j'ai proposé la solution suivante:

public class MyFragment extends Fragment implements
LoaderManager.LoaderCallbacks<Cursor>{

private static final String SPINNER_INIT_VALUE = "Select A Widget";
private Spinner mSpinner;
private int mSpinnerPosition;
private boolean mSpinnerDropDownShowing = false;
private View mSpinnerDropDown;

private MyCursorAdapter mCursorAdapter;

...

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
...

mCursorAdapter = new MyCursorAdapter(getActivity());

mSpinner = (Spinner) rootView.findViewById(R.id.theSpinner);
mSpinner.setOnTouchListener(mSpinnerTouchListener);
mSpinner.setAdapter(mCursorAdapter);

...
}

//Capture the touch events to toggle the spinner's dropdown visibility
private OnTouchListener mSpinnerTouchListener = new View.OnTouchListener() {
    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        if(mSpinnerDropDown != null && mSpinnerDropDownShowing == false){
            mSpinnerDropDownShowing = true;
            mSpinnerDropDown.setVisibility(View.VISIBLE);
        }
        return false;
    }
};

//Capture the click event on the spinner drop down items
protected OnClickListener spinnerItemClick = new OnClickListener(){

    @Override
    public void onClick(View view) {
        String widget = ((TextView) view.findViewById(Android.R.id.text1)).getText().toString();

        if(!widget.equals(SPINNER_INIT_VALUE)){
            if(mCursorAdapter != null){
                Cursor cursor = mCursorAdapter.getCursor();
                if(cursor.moveToFirst()){
                    while(!cursor.isAfterLast()){
                        if(widget.equals(cursor.getString(WidgetQuery.WIDGET_NAME))){

                            ...

                            //Set the spinner to the correct item
                            mSpinnerPosition = cursor.getPosition() + 1;
                            mSpinner.setSelection(mSpinnerPosition);
                            break;
                        }
                        cursor.moveToNext();
                    }
                }
            }
        }

        //Hide the drop down. Not the most elegent solution but it is the only way I could hide/dismiss the drop down
        mSpinnerDropDown = view.getRootView();
        mSpinnerDropDownShowing = false;
        mSpinnerDropDown.setVisibility(View.GONE);
    }
};

private class MyCursorAdapter extends CursorAdapter {

    private final int DISPLACEMENT = 1;
    private final int DEFAULT_ITEM_ID = Integer.MAX_VALUE;

    private Activity mActivity;

    public MyCursorAdapter(Activity activity) {
            super(activity, null, false);
            mActivity = activity;
    }

    //When loading the regular views, inject the defualt item
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if(position == 0){
            if(convertView == null){
                convertView = mActivity.getLayoutInflater().inflate(R.layout.list_item_widget, parent, false);
            }
            return getDefaultItem(convertView);
        }
        return super.getView(position - DISPLACEMENT, convertView, parent);
    }

    //When loading the drop down views, set the onClickListener for each view
    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent){
        View view = super.getDropDownView(position, convertView, parent);
        view.setOnClickListener(spinnerItemClick);
        return view;
    }

    //The special default item that is being injected
    private View getDefaultItem(View convertView){
        TextView text = (TextView) convertView.findViewById(Android.R.id.text1);
        text.setText(SPINNER_INIT_VALUE);
        return convertView;
    }

    @Override
    public long getItemId(int position) {
        if (position == 0) {
            return DEFAULT_ITEM_ID;
        }
        return super.getItemId(position - DISPLACEMENT);
    }

    @Override
    public boolean isEnabled(int position) {
        return position == 0 ? true : super.isEnabled(position - DISPLACEMENT);
    }

    @Override
    public int getViewTypeCount() {
        return super.getViewTypeCount() + DISPLACEMENT;
    }

    @Override
    public int getItemViewType(int position) {
        if (position == 0) {
            return super.getViewTypeCount();
        }

        return super.getItemViewType(position - DISPLACEMENT);
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        return mActivity.getLayoutInflater().inflate(R.layout.list_item_widget, parent, false);
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor){

        if(cursor.isAfterLast()){
            return;
        }

        TextView text = (TextView) view.findViewById(Android.R.id.text1);
        String WidgetName = cursor.getString(WidgetQuery.WIDGET_NAME);
        text.setText(WidgetName);
    }
}
}
0
Jed

si vous faites face à ce problème lorsque vos éléments sont remplis à partir de database-cursor,

la solution la plus simple que j'ai trouvée dans this SO répond:

utilisez UNION dans votre requête d'adaptateur de curseur et ajoutez l'élément supplémentaire avec id = -1 au résultat de la requête, sans l'ajouter réellement à la base de données:

quelque chose comme:

db.rawQuery ("SELECT iWorkerId as _id, nvLastName as nom FROM Worker w UNION SELECT -1 as _id, '' as nom", null);

si l'élément sélectionné est -1, alors c'est la valeur par défaut. Autrement c'est un record de la table.

0
dvrm