Cómo ocultar un elemento en un Spinner de Android

101

Estoy buscando una forma de ocultar un elemento en un widget giratorio de Android. Esto le permitiría simular una ruleta sin elementos seleccionados y garantiza que la devolución de llamada onItemSelected () siempre se llame para cada elemento seleccionado (si el elemento oculto es el "actual"). Normalmente, siempre hay un elemento en la ruleta que no genera una devolución de llamada, a saber, el actual.

Hay algún código en stackoverflow sobre cómo deshabilitar (atenuar) elementos, pero no cómo ocultar elementos por completo como si no existieran.

Después de mucha experimentación, se me ocurrió una solución algo hack-ish que funciona en varias plataformas Android antiguas y nuevas. Tiene algunos inconvenientes cosméticos menores que son difíciles de notar. Todavía me gustaría oír hablar de una solución más oficial, aparte de "no hagas eso con una ruleta".

Esto siempre oculta el primer elemento de la ruleta, pero podría ampliarse fácilmente para ocultar un elemento arbitrario o más de un elemento. Agregue un elemento ficticio que contenga una cadena vacía al comienzo de su lista de elementos giratorios. Es posible que desee establecer la selección de ruleta actual en el elemento 0 antes de que se abra el cuadro de diálogo de la ruleta, esto simulará una ruleta no seleccionada.

Ejemplo de configuración de Spinner con anulación del 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);
Georgie
fuente
¿Qué has encontrado en las otras redes? ¿Qué has intentado hasta ahora?
dldnh
Lo siento, no sé cómo hacerlo.
Louisth
¡Buena solución! Pero creo que la tv.setVisibility(View.GONE);línea es innecesaria. Comentarlo no parece hacer ninguna diferencia (visual), al menos en Android 4.4.2 / KitKit (en un LG / Google Nexus 4).
Matthias
La respuesta en esta pregunta funciona bien ..
Rat-a-tat-a-tat Ratatouille
Esto puede no ser una mejora, pero utilicé setTag(1)en el textView en la posición 0, luego lo usé convertView.getTag() != nullpara determinar si la vista reutilizada era la vista de altura 0 creada para la posición 0 o una vista normal usada para otros elementos giratorios. Esto fue para que pudiera usarlo a super.getDropDownView(position, convertView, parent)veces en lugar de crear siempre una nueva vista.
Daniel Handojo

Respuestas:

49

Para ocultar un elemento arbitrario o más de un elemento, creo que puede implementar su propio adaptador y establecer el índice (o lista de matriz de índices) que desea 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;
     }
 }

Y use su adaptador personalizado cuando cree la lista de elementos.

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

(No he probado el código) espero que ayude.

Aebsubis
fuente
1
Esta no es una solución. La actualización de la pregunta proporciona el código correcto.
dldnh
13
Sin tv.setHeight (0), TextView sigue siendo visible.
v4r
hola, he usado este código para ocultar el primer elemento en mi ruleta, está funcionando bien, mi ruleta me mostrará el segundo elemento, pero cuando hice clic en ese segundo elemento, el texto de ese elemento se establecerá en mi ruleta, yo no quiero mostrar ningún texto en mi ruleta cuando haga clic en ese elemento, por favor guíeme.
Achin
1
Impresionante :) Solución simple :)
Chaitanya
1
Funciona de maravilla..!
Krupa Kakkad
20

Es más fácil ocultar un elemento al final de la lista truncando la lista.

Pero primero debe seleccionarlo para que aparezca en la ruleta y luego verificar si la selección se ha cambiado a uno de los elementos mostrados.

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
fuente
3
Estuve buscando media hora tratando de encontrar un enfoque limpio, y este es, con mucho, el mejor enfoque. Simplemente trunque la lista, pero el elemento realmente existe. Excelente.
KSdev
1
Esto no parece funcionar en Lollipop, la prueba [Seleccione uno] no aparece inicialmente en el Spinner. El mismo código en las versiones anteriores de Android parece hacer lo que todos queremos.
Jonathan Caryl
1
El texto de la ruleta se cambia a 'String3' en el cambio de orientación, incluso la ruleta no se toca. @Romich
Yksh
¿Alguien puede investigar mi pregunta ?
Moeez
5

Para ocultar cualquier elemento en el menú desplegable giratorio, debe pasar la posición del elemento que debe ocultarse según los criterios requeridos. Lo he logrado en un caso de uso de ocultar el elemento que se selecciona del menú desplegable

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

Y configurar el adaptador es algo como esto

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

Al seleccionar algunos elementos del menú desplegable, también se debe cambiar la posición

 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
fuente
1

Solo por interés, hice una solución para usar "Prompt" como pista. Este código está hecho para Xamarin.Android, pero podría ser perfectamente portado a Java en 10 minutos. Úselo como un simple ArrayAdaptersin agregar elementos indexados con 0 o indexados por conteo a la matriz de origen. También se establece SpinnerGeolocation.SelectedItemIden -1 cuando no se elige nada ( hintes el elemento actual).

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
fuente
¿Alguien puede investigar mi pregunta ?
Moeez
1

Encontré esta solución que resolvió mi 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;

       }
    });
Abdul Vajid
fuente
0

Creo que será mejor poner la validación en la Lista de matrices en lugar de en Spinner porque una vez que se filtra el elemento, será seguro agregarlo en Spinner

Akhilesh
fuente
0

Otro enfoque que funcionó mejor para mí es devolver un nuevo objeto de vista en blanco. Este es un enfoque considerablemente limpio ya que no está jugando con elementos de matriz.

Crea tu clase de adaptador ampliando ArrayAdapter

dentro de tu 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;
}
jeet.chanchawat
fuente
0

Esta es una pregunta muy antigua, pero encontré una manera agradable (y probablemente) limpia de no mostrar también los primeros elementos. Inspirado por la respuesta de @ Romich, agregué una lógica similar para omitir el primer elemento (s).

Esto oculta efectivamente un número arbitrario de elementos (1 por defecto). El código solo informa que el tamaño de los objetos a representar es más corto de lo que realmente es y también cambia el índice de elementos a representar, por lo que omitimos un número arbitrario de elementos.

Para simplificar las cosas, he excluido la solución que estoy usando actualmente y que admite la ocultación de la lista de elementos aleatorios, pero que se puede administrar fácilmente con algunos ajustes en el 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)
    }
}
Muhamed Avdić
fuente