studio - tamaño spinner android
Cómo utilizar Android Spinner como una lista desplegable (6)
Me ha costado bastante conseguir que el Spinner de Android gire la cabeza. Después de varios intentos fallidos de implementación, y después de leer muchas preguntas parcialmente similares a las mías, pero sin respuestas satisfactorias , y algunas sin ninguna respuesta, por ejemplo, here y here , finalmente obtengo que un "spinner" en Android no está destinado a ser lo mismo que una "lista desplegable" de aplicaciones de escritorio, o una selección en HTML. Sin embargo, lo que mi aplicación (y supongo que las aplicaciones de todos los demás carteles cuyas preguntas son similares) necesita es algo que funciona como un cuadro desplegable, no como un girador.
Mis dos problemas están relacionados con lo que primero consideré idiosincrasia el OnItemSelectedListener (los he visto como preguntas separadas en este sitio pero no como una):
- Una selección inicial del primer elemento de la lista se activa automáticamente sin la interacción del usuario.
- Cuando el usuario vuelve a seleccionar el elemento que ya estaba seleccionado, se ignora.
Ahora me doy cuenta de que, cuando lo piensas, tiene sentido que esto suceda en una rueda giratoria : tiene que comenzar con un valor predeterminado seleccionado, y lo giras solo para cambiar ese valor, no para "volver a seleccionar" un valor: la documentación dice: "Esta devolución de llamada se invoca solo cuando la nueva posición seleccionada es diferente de la posición seleccionada anteriormente". Y he visto respuestas que sugieren que configuras una bandera para ignorar la primera selección automática. Supongo que podría vivir con eso si no hay otra manera.
Pero como lo que realmente quiero es una lista desplegable que se comporta como una lista desplegable debería (y como los usuarios pueden y deben esperar), lo que necesito es algo como un Spinner que se comporta como un desplegable, como un combo -caja. No me importa ninguna preselección automática (eso debería suceder sin activar a mi oyente), y quiero saber sobre cada selección, incluso si es la misma que antes (después de todo, el usuario seleccionó el mismo elemento nuevamente) .
Entonces ... ¿hay algo en Android que pueda hacer eso, o alguna solución para hacer que un Spinner se comporte como una lista desplegable? Si hay una pregunta como esta en este sitio que no he encontrado y que tiene una respuesta satisfactoria, hágamelo saber (en cuyo caso me disculpo sinceramente por repetir la pregunta).
+1 a la respuesta de David. Sin embargo, aquí hay una sugerencia de implementación que no implica copiar y pegar código de la fuente (que, por cierto, se ve exactamente igual a lo que David publicó en 2.3 también ):
@Override
void setSelectionInt(int position, boolean animate) {
mOldSelectedPosition = INVALID_POSITION;
super.setSelectionInt(position, animate);
}
De esta manera, engañarás al método principal para que piense que es una posición nueva cada vez.
Alternativamente, puede intentar establecer la posición como no válida cuando se hace clic en el botón giratorio y volver a onNothingSelected
. Esto no es tan bueno, porque el usuario no verá qué elemento está seleccionado mientras el diálogo está arriba.
Analicé varios de los problemas mencionados en este hilo antes de PopupMenu cuenta de que el widget PopupMenu es lo que realmente quería. Eso fue fácil de implementar sin los trucos y las soluciones necesarias para cambiar la funcionalidad de un Spinner. PopupMenu era relativamente nuevo cuando se inició este hilo en 2011, pero espero que esto ayude a alguien que busca una funcionalidad similar ahora.
Aquí hay una solución alternativa para diferenciar entre los cambios programáticos (iniciados o no) y los iniciados por el usuario:
Crea tu oyente para el spinner como OnTouchListener y OnItemSelectedListener
public class SpinnerInteractionListener implements AdapterView.OnItemSelectedListener, View.OnTouchListener {
boolean userSelect = false;
@Override
public boolean onTouch(View v, MotionEvent event) {
userSelect = true;
return false;
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
if (userSelect) {
// Your selection handling code here
userSelect = false;
}
}
}
Agregue el oyente al registro giratorio para ambos tipos de eventos
SpinnerInteractionListener listener = new SpinnerInteractionListener();
mSpinnerView.setOnTouchListener(listener);
mSpinnerView.setOnItemSelectedListener(listener);
Esto no manejaría el caso en el que la re-selección del mismo artículo por parte del usuario no dispare el método onItemSelected (que no he observado), pero supongo que eso podría manejarse agregando un código al método onTouch .
De todos modos, los problemas que señaló Amos me estaban volviendo loco antes de pensar en esta solución, así que pensé en compartir lo más ampliamente posible. Hay muchos temas que tratan esto, pero hasta ahora solo he visto otra solución que es similar a esta: https://.com/a/25070696/4556980 .
Mira. No sé si esto te ayudará, pero como parece que estás cansado de buscar una respuesta sin mucho éxito, esta idea te puede ayudar, quién sabe ...
La clase Spinner
se deriva de AbsSpinner
. Dentro de esto, hay este método:
void setSelectionInt(int position, boolean animate) {
if (position != mOldSelectedPosition) {
mBlockLayoutRequests = true;
int delta = position - mSelectedPosition;
setNextSelectedPositionInt(position);
layout(delta, animate);
mBlockLayoutRequests = false;
}
}
Esto es AFAIK tomado de la fuente 1.5 . Tal vez podría verificar esa fuente, ver cómo funciona Spinner / AbsSpinner y tal vez extender esa clase lo suficiente para detectar el método adecuado y no verificar si position != mOldSelectedPosition
.
Quiero decir ... eso es un gran "quizás" con muchos "ifs" (se me viene a la mente con la versión de Android, etc.), pero como parece frustrado (y he estado allí con Android muchas veces), quizás esto pueda dar. algo de "luz". Y asumo que no hay otras respuestas obvias al analizar su investigación anterior.
¡Les deseo buena suerte!
Modificar el Spinner es útil si desea tener varias selecciones simultáneamente en la misma actividad. Si solo desea que el usuario tenga una selección jerárquica, por ejemplo:
¿Qué quieres comer?
Fruta
- Las manzanas
- Plátanos
- Naranjas
Comida rápida
- Hamburguesas
- Papas fritas
- Perros calientes
entonces el ExpandableListView puede ser mejor para usted. Permite al usuario navegar por una jerarquía de diferentes grupos y elegir un elemento secundario. Esto sería similar a tener varios Spinners para que el usuario elija, si no desea una selección simultánea, es decir.
Ok, creo que he encontrado una solución para mi propia situación con la ayuda de la respuesta de David y Felix (creo que la de David ayudó a Felix, que a su vez ayudó a la mía). Pensé que lo publicaría aquí junto con un ejemplo de código en caso de que otra persona también encuentre útil este enfoque. También resuelve mis dos problemas (tanto la selección automática no deseada como el disparador de re-selección deseado).
Lo que he hecho es agregar un elemento ficticio " por favor seleccione " como el primer elemento de mi lista (inicialmente solo para solucionar el problema de selección automática para que pudiera ignorar cuando se seleccionó sin interacción del usuario), y luego, cuando otro Se seleccionó el elemento y he manejado la selección, simplemente restablecí la ruleta al elemento ficticio (que se ignora). Ahora que lo pienso, debería haber pensado en esto hace mucho tiempo antes de decidir publicar mi pregunta en este sitio, pero las cosas siempre son más obvias en retrospectiva ... y encontré que escribir mi pregunta realmente me ayudó a pensar en lo que Quería lograrlo.
Obviamente, si tener un elemento ficticio no se adapta a su situación, esta podría no ser la solución ideal para usted, pero como lo que quería era activar una acción cuando el usuario seleccionaba un valor (y no es necesario que el valor permanezca seleccionado) en mi caso específico), esto funciona bien. Trataré de agregar un ejemplo de código simplificado (puede que no se compile tal como está, arranqué algunos bits de mi código de trabajo y cambié el nombre de las cosas antes de pegarlas, pero espero que tengas la idea) a continuación.
Primero, la actividad de la lista (en mi caso) que contiene el spinner, llamémosla MyListActivity:
public class MyListActivity extends ListActivity {
private Spinner mySpinner;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// TODO: other code as required...
mySpinner = (Spinner) findViewById(R.id.mySpinner);
mySpinner.setAdapter(new MySpinnerAdapter(this));
mySpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> aParentView,
View aView, int aPosition, long anId) {
if (aPosition == 0) {
Log.d(getClass().getName(), "Ignoring selection of dummy list item...");
} else {
Log.d(getClass().getName(), "Handling selection of actual list item...");
// TODO: insert code to handle selection
resetSelection();
}
}
@Override
public void onNothingSelected(AdapterView<?> anAdapterView) {
// do nothing
}
});
}
/**
* Reset the filter spinner selection to 0 - which is ignored in
* onItemSelected() - so that a subsequent selection of another item is
* triggered, regardless of whether it''s the same item that was selected
* previously.
*/
protected void resetSelection() {
Log.d(getClass().getName(), "Resetting selection to 0 (i.e. ''please select'' item).");
mySpinner.setSelection(0);
}
}
Y el código del adaptador giratorio podría tener un aspecto similar a este (de hecho, podría ser una clase interna en la actividad de la lista anterior, si lo prefiere):
public class MySpinnerAdapter extends BaseAdapter implements SpinnerAdapter {
private List<MyListItem> items; // replace MyListItem with your model object type
private Context context;
public MySpinnerAdapter(Context aContext) {
context = aContext;
items = new ArrayList<MyListItem>();
items.add(null); // add first dummy item - selection of this will be ignored
// TODO: add other items;
}
@Override
public int getCount() {
return items.size();
}
@Override
public Object getItem(int aPosition) {
return items.get(aPosition);
}
@Override
public long getItemId(int aPosition) {
return aPosition;
}
@Override
public View getView(int aPosition, View aView, ViewGroup aParent) {
TextView text = new TextView(context);
if (aPosition == 0) {
text.setText("-- Please select --"); // text for first dummy item
} else {
text.setText(items.get(aPosition).toString());
// or use whatever model attribute you''d like displayed instead of toString()
}
return text;
}
}
Supongo que (no lo he intentado) se podría lograr el mismo efecto utilizando setSelected(false)
lugar de setSelection(0)
, pero volver a configurarlo como "seleccione" cumple mis propósitos. Y, "mira, mamá, no hay bandera!" (Aunque supongo que ignorar 0
selecciones no es tan diferente).
Con suerte, esto puede ayudar a alguien más con un caso de uso similar. :-) Para otros casos de uso, la respuesta de Félix puede ser más adecuada (¡gracias Félix!).