Como esconder um item em um Android Spinner


101

Estou procurando uma maneira de ocultar um item em um widget giratório do Android. Isso permitiria que você simule um botão giratório sem nenhum item selecionado e garante que o retorno de chamada onItemSelected () seja sempre chamado para cada item selecionado (se o item oculto for o "atual"). Normalmente, há sempre um item no botão giratório que não gera um retorno de chamada, ou seja, o atual.

Há algum código em stackoverflow sobre como desabilitar (acinzentar) itens, mas não como ocultar itens completamente como se eles não existissem.

Depois de muita experimentação, eu vim com uma solução um tanto hack-ish que funciona em várias plataformas Android antigas e novas. Tem algumas desvantagens estéticas menores que são difíceis de notar. Eu ainda gostaria de ouvir uma solução mais oficial, diferente de "não faça isso com um spinner".

Isso sempre oculta o primeiro item no botão giratório, mas pode ser facilmente estendido para ocultar um item arbitrário ou mais de um item. Adicione um item fictício contendo uma string vazia no início de sua lista de itens giratórios. Você pode querer definir a seleção do botão giratório atual para o item 0 antes que a caixa de diálogo do botão giratório seja aberta, isso simulará um botão giratório não selecionado.

Exemplo de configuração do spinner com modificação do método 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);

o que você encontrou nas outras interwebs? O que você tentou até agora?
dldnh

Desculpe lah, eu não sei fazer.
Louisth

Ótima solução! Mas acho que a tv.setVisibility(View.GONE);linha é desnecessária. Comentar não parece fazer nenhuma diferença (visual), pelo menos no Android 4.4.2 / KitKit (em um LG / Google Nexus 4).
Matias

A resposta a esta pergunta funciona bem ..
Rat-a-tat-a-tat Ratatouille

Isso pode não ser uma melhoria, mas usei setTag(1)no textView na posição 0 e depois usei convertView.getTag() != nullpara determinar se a visualização reutilizada era a visualização de altura 0 criada para a posição 0 ou uma visualização normal usada para outros itens giratórios. Isso era para que eu pudesse usar super.getDropDownView(position, convertView, parent)algumas vezes em vez de sempre criar uma nova visualização.
Daniel Handojo

Respostas:


49

Para ocultar um item arbitrário ou mais de um item, acho que você pode implementar seu próprio adaptador e definir o índice (ou lista de matriz de índices) que deseja ocultar.

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 use seu adaptador personalizado ao criar a lista de itens.

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

(Eu não testei o código) espero que ajude.


1
Esta não é uma solução. A atualização da pergunta fornece o código correto.
dldnh

13
Sem tv.setHeight (0), o TextView ainda é visível.
v4r

oi, usei este código para ocultar o primeiro item no meu botão giratório, ele está funcionando bem, meu botão giratório me mostrará o segundo item, mas quando eu cliquei no segundo item, o texto nesse item será definido para o meu botão giratório, eu não quero mostrar nenhum texto no meu botão giratório quando cliquei nesse item, por favor, me orientar?
Achin

1
Incrível :) Solução simples :)
Chaitanya

1
Funciona como um encanto..!
Krupa Kakkad

20

É mais fácil ocultar um item no final da lista truncando a lista.

Mas você deve selecioná-lo primeiro para que apareça no botão giratório e, em seguida, verificar se a seleção foi alterada para um dos itens exibidos.

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
Eu estive procurando por meia hora tentando encontrar uma abordagem limpa, e esta é de longe a melhor abordagem. Apenas trunque a lista, mas o item realmente existe. Excelente.
KSdev

1
Isso não parece funcionar no Lollipop, o teste [Selecione um] não aparece inicialmente no Spinner. O mesmo código em versões mais antigas do Android parece fazer o que todos nós queremos.
Jonathan Caryl

1
O texto giratório é alterado para 'String3' na mudança de orientação, mesmo o girador permanece intocado. @Romich
Yksh

qualquer um pode olhar para a minha pergunta ?
Moeez de

5

Para ocultar qualquer item no menu suspenso giratório, você precisa passar a posição do item que precisa ser escondido com base nos critérios exigidos. Eu consegui isso em um caso de uso de ocultar o item que é selecionado no menu suspenso

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 configurar o adaptador é algo assim

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

Ao selecionar alguns itens da lista suspensa, também a posição precisa ser alterada

 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

Apenas por interesse, criei uma solução para usar "Prompt" como uma dica. Este código é feito para Xamarin.Android, mas poderia ser perfeitamente portado para Java em 10 minutos. Use-o como um simples, ArrayAdaptersem adicionar itens com índice 0 ou com contagem de itens ao array de origem. Também é definido SpinnerGeolocation.SelectedItemIdcomo -1 quando nada é escolhido ( hinté o item atual).

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

qualquer um pode olhar para a minha pergunta ?
Moeez de

1

Encontrei esta solução que resolveu meu 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

Acho que será melhor colocar a validação na Lista de Array em vez de no Spinner porque uma vez que o item é filtrado, será seguro adicionar no Spinner


0

Outra abordagem que funcionou melhor para mim é retornar um novo objeto de visualização em branco. Esta é uma abordagem consideravelmente limpa, pois você não está brincando com elementos de array.

Crie sua extensão de classe de adaptador ArrayAdapter

dentro do seu método

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

Esta é uma pergunta muito antiga, mas eu encontrei uma maneira legal (e provavelmente) limpa de não mostrar os primeiros itens também. Inspirado pela resposta de @Romich, adicionei uma lógica semelhante para pular o (s) primeiro (s) item (ns).

Isso efetivamente oculta um número arbitrário de itens (1 por padrão). O código apenas relata que o tamanho dos objetos a serem renderizados é menor do que realmente é e também altera o índice de itens a serem renderizados, de modo que pulamos um número arbitrário de itens.

Para manter as coisas simples, excluí a solução que estou usando atualmente, que oferece suporte à ocultação de lista de itens aleatórios, mas que pode ser facilmente gerenciada por alguns ajustes no código.

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)
    }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.