El otro día nos reportaron un bug diciendo que una de las pantallas de la aplicación hacía un pequeño parpadeo, en concreto esto pasaba en la pantalla de detalle de un item.
Nada más entrar en dicha pantalla, vi a lo que se referían, pero eso no pasaba antes ¿que había cambiado?
Lo que había cambiado es que ahora el usuario podía modificar la categoría del ítem desde esa pantalla, para hacer eso habíamos añadido a la vista un
Spinner
que permitía seleccionarla dentro de un listado y una vez seleccionada se repintaba la pantalla con nuevos datos relacionados con la categoría.
El problema no fue nada fácil de identificar, lo que estaba pasando es que estábamos haciendo un "mal uso" de
Spinner.setSelection()
, básicamente no estábamos llamando a Spinner.setSelection()
al mismo tiempo que añadíamos las categorías al adapter.
Seguro que con algo de código queda más claro:
Este código llamará a
OnItemSelectedListener.onItemSelected
diciendo que el elemento 0 ha sido seleccionado.
spinner = (Spinner) findViewById(R.id.spinner);
spinner.setOnItemSelectedListener(this);
adapter = new ArrayAdapter<String>(this, R.layout.simple_spinner_item, items);
spinner.setAdapter(adapter);
¡Pero eso no es lo que esperamos! lo que esperamos es que
onItemSelected
séa llamado cuando el usuario selecciona un item del Spinner
o en todo caso podríamos esperar que sea llamado cuando hacemos spinner.setSelection(position);
diciendo que el elemento seleccionado fue el position
.
En nuestro caso, el usuario podía haber elegido previamente una categoría, así que en algún momento tras cargar los datos de la base de datos hacíamos
spinner.setSelection(position);
y "et voilà", onItemSelected
es llamado dos veces, la primera diciendo que se seleccionó el elemento 0 y la segunda que se seleccionó el elemento position
.
Llegado a este punto decidí mirar en la red como solucionaba la gente este problema y encontré cosas muy interesantes (y locas):
- Mantener contadores por cada elemento con OnItemSelectedListener que tengas en la pantalla para obviar la primera llamada a
onItemSelected
. - Crearse un custom adapter y añadir
spinner.setOnTouchListener()
para controlar cuando el usuario ha tocado la pantalla primero. - Crearte un custom view, sobre escribir el método
onClick
y llamar aCustomOnItemClickListener.onItemClick
en él.
Ninguna de estas opciones me convencía (encontré más algunas más que tampoco), así que decidí investigar un poco más y descubrí que no se puede evitar que
onItemSelected
sea llamado cuando se añade el adapter
al Spinner
si el adapter
ya tiene los items, pero si se podía hacer que se llamase con el elemento seleccionado correcto si spinnerView.setSelection(position)
es llamado inmediatamente después de añadir los elementos a la vista/adapter
. A mí se me ha ocurrido hacerlo de dos formas:Primera solución
Creamos el
adapter
con los items y lo añadimos al Spinner
cuando ya sabemos el elemento que debe ser seleccionado.
adapter = new ArrayAdapter<>(this, R.layout.simple_spinner_item, items);
spinner.setAdapter(adapter);
spinner.setSelection(position);
Segunda solución
Creamos el
adapter
sin los items y lo añadimos al Spinner
.
adapter = new ArrayAdapter<>(this, R.layout.simple_spinner_item);
spinner.setAdapter(adapter);
Y cuando ya sabemos el elemento que debe ser seleccionado.
spinner.setSelection(10);
adapter.addAll(items);
Conclusión
Sería perfecto poder poner el
AdapterView.OnItemSelectedListener
justo después de añadir los items al adapter
y que no fuese disparado, pero no se puede, así que dentro de lo malo esto debería ser una solución aceptable.Código completo solución 1
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base_layout);
final Spinner spinner = (Spinner) findViewById(R.id.spinner);
spinner.setOnItemSelectedListener(this);
final ArrayAdapter<String> adapter =
new ArrayAdapter<>(this, R.layout.simple_spinner_item, items);
spinner.setAdapter(adapter);
spinner.setSelection(10);
}
Código completo solución 2
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base_layout);
final Spinner spinner = (Spinner) findViewById(R.id.spinner);
spinner.setOnItemSelectedListener(this);
final ArrayAdapter<String> adapter =
new ArrayAdapter<>(this, R.layout.simple_spinner_item);
spinner.setAdapter(adapter);
findViewById(R.id.select_item).setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
spinner.setSelection(10);
adapter.addAll(items);
}
});
}
No hay comentarios:
Publicar un comentario