Come nascondere un elemento in uno Spinner Android


101

Sto cercando un modo per nascondere un elemento in un widget di rotazione di Android. Ciò consentirebbe di simulare uno spinner senza elementi selezionati e garantisce che il callback onItemSelected () venga sempre chiamato per ogni elemento selezionato (se l'elemento nascosto è quello "corrente"). Normalmente c'è sempre un elemento nello spinner che non genera un callback, ovvero quello corrente.

C'è del codice su stackoverflow su come disabilitare (in grigio) gli elementi, ma non su come nascondere completamente gli elementi come se non esistessero.

Dopo molte sperimentazioni ho escogitato una soluzione un po 'hacker che funziona su varie piattaforme Android vecchie e nuove. Presenta alcuni piccoli inconvenienti estetici che sono difficili da notare. Mi piacerebbe comunque sentire una soluzione più ufficiale, diversa da "non farlo con uno spinner".

Questo nasconde sempre il primo elemento nello spinner, ma potrebbe essere facilmente esteso per nascondere un elemento arbitrario o più di un elemento. Aggiungi un elemento fittizio contenente una stringa vuota all'inizio dell'elenco di elementi di selezione. Potresti voler impostare la selezione attuale dello spinner sull'elemento 0 prima che la finestra di dialogo dello spinner si apra, questo simulerà uno spinner non selezionato.

Esempio di configurazione Spinner con override del metodo 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);

cosa hai trovato sugli altri interweb? Cosa hai provato fino ad ora?
dldnh

Mi dispiace lah, non so come fare.
Louisth

Bella soluzione! Ma penso che la tv.setVisibility(View.GONE);linea non sia necessaria. Commentarlo non sembra fare alcuna differenza (visiva), almeno su Android 4.4.2 / KitKit (su un LG / Google Nexus 4).
Matthias

La risposta a questa domanda funziona bene ..
Rat-a-tat-a-tat Ratatouille

Questo potrebbe non essere un miglioramento, ma l'ho usato setTag(1)sul textView alla posizione 0, quindi l'ho usato convertView.getTag() != nullper determinare se la vista riutilizzata era la vista di altezza 0 creata per la posizione 0 o una vista normale usata per altri elementi di selezione. Questo era così che potevo usare a super.getDropDownView(position, convertView, parent)volte invece di creare sempre una nuova vista.
Daniel Handojo

Risposte:


49

Per nascondere un elemento arbitrario o più di un elemento penso che tu possa implementare il tuo adattatore e impostare l'indice (o l'elenco degli indici dell'array) che desideri nascondere.

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

E usa il tuo adattatore personalizzato quando crei l'elenco di elementi.

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

(Non ho testato il codice) spero che aiuti.


1
Questa non è una soluzione. L'aggiornamento alla domanda fornisce il codice corretto.
dldnh

13
Senza tv.setHeight (0), TextView è ancora visibile.
v4r

ciao, ho usato questo codice per nascondere il primo elemento nel mio spinner, funziona bene, il mio spinner mi mostrerà il secondo elemento, ma quando ho fatto clic su quel secondo elemento il testo su quell'elemento verrà impostato sul mio spinner, io non voglio mostrare alcun testo sul mio spinner quando ho cliccato su quell'elemento, per favore guidami?
Achin

1
Fantastico :) Soluzione semplice :)
Chaitanya

1
Funziona come un fascino ..!
Krupa Kakkad

20

È più facile nascondere un elemento alla fine dell'elenco troncando l'elenco.

Ma devi prima selezionarlo in modo che appaia nella casella di selezione, quindi verificare se la selezione è stata modificata in uno degli elementi visualizzati.

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
Ho cercato per mezz'ora di trovare un approccio pulito, e questo è di gran lunga l'approccio migliore. Basta troncare l'elenco, ma l'elemento esiste effettivamente. Eccellente.
KSdev

1
Questo non sembra funzionare in Lollipop, il test [Seleziona uno] non viene visualizzato inizialmente nello Spinner. Lo stesso codice nelle versioni precedenti di Android sembra fare ciò che tutti vogliamo.
Jonathan Caryl

1
Il testo dello spinner viene modificato in "String3" al cambio di orientamento anche lo spinner non viene toccato. @Romich
Yksh

qualcuno può esaminare la mia domanda ?
Moeez

5

Per nascondere qualsiasi elemento nel menu a discesa della casella di selezione, è necessario passare la posizione dell'elemento che deve essere nascosto in base ai criteri richiesti. Ho ottenuto questo risultato in un caso d'uso di nascondere l'elemento selezionato dal menu a discesa

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

E l'impostazione dell'adattatore è qualcosa del genere

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

Selezionando alcuni elementi dal menu a discesa anche la posizione deve essere modificata

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

        }
    });

1

Solo per interesse, ho creato una soluzione per utilizzare "Prompt" come suggerimento. Questo codice è fatto per Xamarin.Android, ma potrebbe essere perfettamente portato su Java in 10 minuti. Usalo come un semplice ArrayAdaptersenza aggiungere elementi indicizzati 0 o conteggio all'array di origine. Inoltre è impostato SpinnerGeolocation.SelectedItemIdsu -1 quando non viene scelto nulla ( hintè l'elemento corrente).

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

qualcuno può esaminare la mia domanda ?
Moeez

1

Ho trovato questa soluzione che ha risolto il mio problema.

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;

       }
    });

0

Penso che sarà meglio inserire la convalida nell'elenco degli array piuttosto che su Spinner perché una volta filtrato l'elemento, sarà sicuro aggiungerlo in Spinner


0

Un altro approccio che ha funzionato meglio per me è restituire un nuovo oggetto di visualizzazione vuota. Questo è un approccio decisamente pulito dato che non stai giocando con gli elementi dell'array.

Crea la tua classe dell'adattatore extending ArrayAdapter

all'interno del tuo metodo

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

0

Questa è una domanda molto vecchia, ma ho trovato un modo carino (e probabilmente) pulito per non mostrare anche i primi elementi. Ispirato dalla risposta di @ Romich, ho aggiunto una logica simile per saltare i primi elementi.

Ciò nasconde effettivamente un numero arbitrario di elementi (1 per impostazione predefinita). Il codice riporta solo che la dimensione degli oggetti da renderizzare è più corta di quanto non sia in realtà e cambia anche l'indice degli elementi da renderizzare, quindi saltiamo un numero arbitrario di elementi.

Per mantenere le cose semplici ho escluso la soluzione che sto attualmente utilizzando che supporta l'occultamento di un elenco di elementi casuali, ma che può essere facilmente gestita con alcune modifiche al codice.

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)
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.