Comment masquer un élément dans un Spinner Android

101

Je recherche un moyen de masquer un élément dans un widget spinner Android. Cela vous permettrait de simuler un spinner sans élément sélectionné et garantit que le rappel onItemSelected () est toujours appelé pour chaque élément sélectionné (si l'élément masqué est l'élément "actuel"). Normalement, il y a toujours un élément dans le spinner qui ne génère pas de rappel, à savoir celui en cours.

Il existe du code sur stackoverflow pour savoir comment désactiver (grisé) les éléments, mais pas comment masquer complètement les éléments comme s'ils n'existaient pas.

Après de nombreuses expérimentations, j'ai trouvé une solution quelque peu piratée qui fonctionne sur diverses anciennes et nouvelles plates-formes Android. Il présente quelques inconvénients cosmétiques mineurs difficiles à remarquer. J'aimerais toujours entendre parler d'une solution plus officielle, autre que "ne faites pas ça avec un spinner".

Cela masque toujours le premier élément dans le spinner, mais pourrait assez facilement être étendu pour masquer un élément arbitraire ou plus d'un élément. Ajoutez un élément factice contenant une chaîne vide au début de votre liste d'éléments spinner. Vous souhaiterez peut-être définir la sélection actuelle de la double flèche sur l'élément 0 avant que la boîte de dialogue de la double flèche ne s'ouvre, cela simulera une double flèche non sélectionnée.

Exemple de configuration de Spinner avec remplacement de méthode ArrayAdapter:

List<String> list = new ArrayList<String>();
list.add("");   //  Initial dummy entry
list.add("string1");
list.add("string2");
list.add("string3");

// Populate the spinner using a customized ArrayAdapter that hides the first (dummy) entry
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, list) {
    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent)
    {
        View v = null;

        // If this is the initial dummy entry, make it hidden
        if (position == 0) {
            TextView tv = new TextView(getContext());
            tv.setHeight(0);
            tv.setVisibility(View.GONE);
            v = tv;
        }
        else {
            // Pass convertView as null to prevent reuse of special case views
            v = super.getDropDownView(position, null, parent);
        }

        // Hide scroll bar because it appears sometimes unnecessarily, this does not prevent scrolling 
        parent.setVerticalScrollBarEnabled(false);
        return v;
    }
};

dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mySpinner.setAdapter(dataAdapter);
Georgie
la source
qu'avez-vous trouvé sur les autres interwebs? Qu'est-ce que tu as essayé jusque-là?
dldnh
Désolé lah, je ne sais pas comment faire.
Louisth
Belle solution! Mais je pense que la tv.setVisibility(View.GONE);ligne est inutile. Le commenter ne semble pas faire de différence (visuelle), du moins sur Android 4.4.2 / KitKit (sur un LG / Google Nexus 4).
Matthias
La réponse à cette question fonctionne bien.
Rat-a-tat-a-tat Ratatouille
Ce n'est peut-être pas une amélioration, mais j'ai utilisé setTag(1)sur le textView à la position 0, puis utilisé convertView.getTag() != nullpour déterminer si la vue réutilisée était la vue de hauteur 0 créée pour la position 0 ou une vue normale utilisée pour d'autres éléments de spinner. C'était si je pouvais utiliser super.getDropDownView(position, convertView, parent)parfois au lieu de créer toujours une nouvelle vue.
Daniel Handojo

Réponses:

49

Pour masquer un élément arbitraire ou plusieurs éléments, je pense que vous pouvez implémenter votre propre adaptateur et définir l'index (ou la liste des index du tableau) que vous souhaitez masquer.

public class CustomAdapter extends ArrayAdapter<String> {

     private int hidingItemIndex;

     public CustomAdapter(Context context, int textViewResourceId, String[] objects, int hidingItemIndex) {
         super(context, textViewResourceId, objects);
         this.hidingItemIndex = hidingItemIndex;
     }

     @Override
     public View getDropDownView(int position, View convertView, ViewGroup parent) {
         View v = null;
         if (position == hidingItemIndex) {
             TextView tv = new TextView(getContext());
             tv.setVisibility(View.GONE);
             v = tv;
         } else {
             v = super.getDropDownView(position, null, parent);
         }
         return v;
     }
 }

Et utilisez votre adaptateur personnalisé lorsque vous créez la liste des éléments.

List<String> list = new ArrayList<String>();
list.add("");   //  Initial dummy entry
list.add("string1");
list.add("string2");
list.add("string3");

int hidingItemIndex = 0;

CustomAdapter dataAdapter = new CustomAdapter(this, android.R.layout.simple_spinner_item, list, hidingItemIndex);

dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mySpinner.setAdapter(dataAdapter);

(Je n'ai pas testé le code) j'espère que cela aide.

Aebsubis
la source
1
Ce n'est pas une solution. La mise à jour de la question fournit le code correct.
dldnh
13
Sans tv.setHeight (0), le TextView est toujours visible.
v4r
salut, j'ai utilisé ce code pour masquer le premier élément de mon spinner, cela fonctionne bien, mon spinner me montrera le deuxième élément, mais lorsque j'ai cliqué sur ce deuxième élément, le texte de cet élément sera défini sur mon spinner, je je ne veux pas afficher de texte sur mon spinner lorsque j'ai cliqué sur cet élément, veuillez me guider?
Achin le
1
Génial :) Solution simple :)
Chaitanya
1
Fonctionne comme un charme ..!
Krupa Kakkad
20

Il est plus facile de masquer un élément à la fin de la liste en tronquant la liste.

Mais vous devez d'abord le sélectionner pour qu'il apparaisse dans la double flèche, puis vérifier si la sélection a été remplacée par l'un des éléments affichés.

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
Romich
la source
3
Je cherchais pendant une demi-heure à essayer de trouver une approche propre, et c'est de loin la meilleure approche. Il suffit de tronquer la liste, mais l'élément existe réellement. Excellent.
KSdev
1
Cela ne semble pas fonctionner dans Lollipop, le test [Select one] n'apparaît pas initialement dans le Spinner. Le même code sur les anciennes versions d'Android semble faire ce que nous voulons tous.
Jonathan Caryl
1
Le texte de la flèche est changé en «String3» lors du changement d'orientation, même la flèche n'est pas touchée. @Romich
Yksh
quelqu'un peut-il examiner ma question ?
Moeez
5

Pour masquer un élément dans la liste déroulante de la flèche, vous devez transmettre la position de l'élément qui doit être masqué en fonction des critères requis. J'ai réalisé cela dans un cas d'utilisation consistant à masquer l'élément sélectionné dans la liste déroulante

public class CustomAdapter extends ArrayAdapter<String> {

private List<String> dates;
private int hideItemPostion;

public CustomAdapter (Context context, int resource, List<String> dates) {
    super(context, resource,dates);
    this.dates=dates;
}
public void setItemToHide(int itemToHide)
{
    this.hideItemPostion =itemToHide;
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
    View v = null;
    if (position == hideItemPostion) {
        TextView tv = new TextView(getContext());
        tv.setVisibility(View.GONE);
        tv.setHeight(0);
        v = tv;
        v.setVisibility(View.GONE);
    }
    else
        v = super.getDropDownView(position, null, parent);
    return v;
}}

Et régler l'adaptateur est quelque chose comme ça

final CustomAdapter dataAdapter = new CustomAdapter(this,R.layout.spinner_item,dates);
    dataAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
    spinner.setAdapter(dataAdapter);
    dataAdapter.setItemToHide(0);

En sélectionnant certains éléments dans la liste déroulante, la position doit également être modifiée

 spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> adapterView, View view, final int i, long l) {
        dataAdapter.notifyDataSetChanged();
            mEPGDateSelector.setSelection(i);
            dataAdapter.setItemToHide(i);}

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

        }
    });
Jiju Induchoodan
la source
1

Juste pour l'intérêt, j'ai fait une solution pour utiliser "Prompt" comme indice. Ce code est fait pour Xamarin.Android, mais il pourrait être parfaitement porté vers Java en 10 minutes. Utilisez-le comme un simple ArrayAdaptersans ajouter d'éléments indexés à 0 ou à nombre d'index au tableau source. Il est également mis SpinnerGeolocation.SelectedItemIdà -1 lorsque rien n'est choisi ( hintest l'élément actuel).

public class ArrayAdapterWithHint<T>: ArrayAdapter<T>
{
    protected bool HintIsSet = false;
    protected int HintResource = 0;

    public ArrayAdapterWithHint(Context context, int textViewResourceId,
                   T[] objects)
        : base(context, textViewResourceId, objects)
    {
    }
    public ArrayAdapterWithHint(Context context, int hintResource,
                   int textViewResourceId, T[] objects)
        : base(context, textViewResourceId, objects)
    {
        HintResource = hintResource;
    }
    public ArrayAdapterWithHint(Context context, int textViewResourceId,
             IList<T> objects)
        : base(context, textViewResourceId, objects)
    {
    }
    public ArrayAdapterWithHint(Context context, int hintResource,
                    int textViewResourceId, IList<T> objects)
        : base(context, textViewResourceId, objects)
    {
        HintResource = hintResource;
    }

    public override View GetDropDownView(int position, View convertView,
                ViewGroup parent)
    {
        if (HintIsSet)
            return base.GetDropDownView(position + 1,
                               convertView, parent);
        return base.GetDropDownView(position, convertView, parent);
    }

    public override View GetView(int position, View convertView,
                      ViewGroup parent)
    {
        if (!HintIsSet && parent is Spinner && 
                    !string.IsNullOrWhiteSpace((parent as Spinner).Prompt))
        {
            Insert((parent as Spinner).Prompt, 0);
            HintIsSet = true;
            (parent as Spinner).SetSelection(base.Count - 1);
        }
        if (HintIsSet && position >= base.Count - 1)
        {
            View hintView = base.GetView(0, convertView, parent);
            if (hintView is TextView)
                (hintView as TextView).SetTextAppearance(
                                                     Context, HintResource);
            return hintView;
        }
        if (HintIsSet && position < base.Count - 1)
            return base.GetView(position + 1, convertView, parent);
        return base.GetView(position, convertView, parent);
    }

    public override long GetItemId(int position)
    {
        if (HintIsSet)
        {
            if (position >= base.Count - 1)
                return -1;
            return position;
        }
        return base.GetItemId(position);
    }

    public override int Count
    {
        get
        {
            return base.Count > 0 && HintIsSet ? base.Count - 1 : base.Count;
        }
    }
}
KD
la source
quelqu'un peut-il examiner ma question ?
Moeez
1

J'ai trouvé cette solution qui a résolu mon problème.

final Spinner mySpinner = (Spinner)findViewById(R.id.spinner_triptype);

   final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,R.layout.spinner_item, R.id.weekofday, triptype_initial);

   final ArrayAdapter<String> adapter_temp = new ArrayAdapter<String>
(this,R.layout.spinner_item, R.id.weekofday, triptype_array);


   mySpinner.setAdapter(adapter);
    mySpinner.setOnTouchListener(new View.OnTouchListener() {
       @Override
       public boolean onTouch(View v, MotionEvent event) {
       // display your error popup here
        if(flag_spinner_isFirst){
           mySpinner.setAdapter(adapter_temp);
           flag_spinner_isFirst = false;
          }
           v.onTouchEvent(event);
           return true;

       }
    });
Abdul Vajid
la source
0

Je pense qu'il vaudra mieux mettre la validation sur la liste des tableaux plutôt que sur Spinner car une fois l'élément filtré, il sera sûr de l'ajouter dans Spinner

Akhilesh
la source
0

Une autre approche qui a fonctionné le mieux pour moi est de renvoyer un nouvel objet de vue vide. Il s'agit d'une approche très claire car vous ne jouez pas avec des éléments de tableau.

Créez votre classe d'adaptateur en étendant ArrayAdapter

à l'intérieur de votre méthode

public View getView(int position, View convertView, ViewGroup parent) {
    View row = getCustomView();
    if(position==0) // put the desired check here.
         {
            row  = new View(context);
         }
    }
    return row;
}
jeet.chanchawat
la source
0

C'est une question très ancienne, mais j'ai trouvé un moyen agréable (et probablement) propre de ne pas afficher les premiers éléments également. Inspiré par la réponse de @ Romich, j'ai ajouté une logique similaire pour ignorer le (s) premier (s) élément (s).

Cela masque effectivement un nombre arbitraire d'éléments (1 par défaut). Le code signale seulement que la taille des objets à rendre est plus courte qu'elle ne l'est réellement et modifie également l'index des éléments à rendre afin que nous sautions un nombre arbitraire d'éléments.

Pour simplifier les choses, j'ai exclu la solution que j'utilise actuellement, qui prend en charge le masquage de la liste d'éléments aléatoires, mais qui peut facilement être gérée par quelques modifications du code.

class ArrayAdapterCustom(context: Context, textViewResourceId: Int, vararg objects: String)
    : ArrayAdapter<String>(context, textViewResourceId, objects) {

    //Can skip first n items (skip 1 as default)
    var hideFirstItemsCount = 1

    override fun getCount(): Int {
        return super.getCount() - hideFirstItemsCount
    }

    override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
        return super.getDropDownView(position + hideFirstItemsCount, convertView, parent)
    }
}
Muhamed Avdić
la source