He pensado en formas poco elegantes de resolver esto, pero sé que me falta algo.
Mi se onItemSelected
dispara inmediatamente sin ninguna interacción con el usuario, y este es un comportamiento no deseado. Deseo que la interfaz de usuario espere hasta que el usuario seleccione algo antes de hacer algo.
Incluso intenté configurar el oyente en el onResume()
, esperando que eso ayudara, pero no lo hace.
¿Cómo puedo evitar que esto se active antes de que el usuario pueda tocar el control?
public class CMSHome extends Activity {
private Spinner spinner;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Heres my spinner ///////////////////////////////////////////
spinner = (Spinner) findViewById(R.id.spinner);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
this, R.array.pm_list, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
};
public void onResume() {
super.onResume();
spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());
}
public class MyOnItemSelectedListener implements OnItemSelectedListener {
public void onItemSelected(AdapterView<?> parent,
View view, int pos, long id) {
Intent i = new Intent(CMSHome.this, ListProjects.class);
i.putExtra("bEmpID", parent.getItemAtPosition(pos).toString());
startActivity(i);
Toast.makeText(parent.getContext(), "The pm is " +
parent.getItemAtPosition(pos).toString(), Toast.LENGTH_LONG).show();
}
public void onNothingSelected(AdapterView parent) {
// Do nothing.
}
}
}
android
spinner
android-spinner
FauxReal
fuente
fuente
Spinner
vacío y dentroonItemSelected
pueda detectar si la Cadena no está vacíastartActivity
!Respuestas:
Hubiera esperado que su solución funcionara, aunque pensé que el evento de selección no se dispararía si configura el adaptador antes de configurar el oyente.
Dicho esto, una simple bandera booleana le permitiría detectar el primer evento de selección deshonesto e ignorarlo.
fuente
onResume()
yonPostResume()
, por lo tanto, todos los ganchos normales se han completado para cuando se produce el diseño.El uso de Runnables es completamente incorrecto.
Usar
setSelection(position, false);
en la selección inicial antessetOnItemSelectedListener(listener)
De esta manera, configura su selección sin animación, lo que hace que se llame al oyente seleccionado en el elemento. Pero el oyente es nulo, por lo que no se ejecuta nada. Entonces su oyente es asignado.
Entonces sigue esta secuencia exacta:
fuente
Refiriéndose a la respuesta de Dan Dyer, intente registrar el
OnSelectListener
en unpost(Runnable)
método:Al hacer eso por mí, el comportamiento deseado finalmente ocurrió.
En este caso, también significa que el oyente solo dispara en un elemento modificado.
fuente
onCreate()
,onResume()
etc. En ese caso, es un truco fantástico, sin peligro de condición de carrera. Normalmente uso este trucoonCreate()
justo después del código de diseño.Creé un pequeño método de utilidad para cambiar la
Spinner
selección sin notificar al usuario:Inhabilita al oyente, cambia la selección y vuelve a habilitar el oyente después de eso.
El truco es que las llamadas son asíncronas al hilo de la interfaz de usuario, por lo que debe hacerlo en publicaciones de manejador consecutivas.
fuente
setSpinnerSelectionWithoutCallingListener
dos veces rápidamente, de modo que la segunda llamada se realiza mientras la primera ya ha configurado el oyentenull
, su ruleta estará atascada con unnull
oyente para siempre. Propongo la siguiente solución: agregarif (listener == null) return;
despuésspinner.setSelection(selection)
.Desafortunadamente, parece que las dos soluciones más comúnmente sugeridas para este problema, a saber, contar las ocurrencias de devolución de llamada y publicar un Runnable para configurar la devolución de llamada en un momento posterior, pueden fallar cuando, por ejemplo, las opciones de accesibilidad están habilitadas. Aquí hay una clase auxiliar que soluciona estos problemas. Más explicaciones se encuentran en el bloque de comentarios.
fuente
He tenido MUCHOS problemas con el disparo de la ruleta cuando no quería, y todas las respuestas aquí no son confiables. Funcionan, pero solo a veces. Eventualmente se encontrará con escenarios donde fallarán e introducirán errores en su código.
Lo que funcionó para mí fue almacenar el último índice seleccionado en una variable y evaluarlo en el oyente. Si es lo mismo que el nuevo índice seleccionado, no haga nada y regrese, de lo contrario, continúe con el oyente. Hacer esto:
Confía en mí cuando digo esto, esta es, con mucho, la solución más confiable. Un truco, pero funciona!
fuente
Estaba en una situación similar y tengo una solución simple que funciona para mí.
Parecen métodos
setSelection(int position)
ysetSelected(int position, boolean animate)
tienen una implementación interna diferente.Cuando usa el segundo método
setSelected(int position, boolean animate)
con una bandera de animación falsa, obtiene la selección sin disparar alonItemSelected
oyente.fuente
setSelection(int position, boolean animate);
onItemSelected
en API23Solo para dar pistas sobre el uso de onTouchListener para distinguir entre llamadas automáticas al setOnItemSelectedListener (que son parte de la inicialización de la actividad, etc.) frente a llamadas activadas por la interacción real del usuario, hice lo siguiente después de probar algunas otras sugerencias aquí y descubrió que funcionaba bien con la menor cantidad de líneas de código.
Simplemente configure un campo booleano para su Actividad / Fragmento como:
Luego, justo antes de configurar el setOnItemSelectedListener de su ruleta, configure un onTouchListener:
fuente
fuente
Después de arrancarme el pelo durante mucho tiempo, he creado mi propia clase de Spinner. Le agregué un método que desconecta y conecta al oyente apropiadamente.
Úselo en su XML así:
Todo lo que tiene que hacer es recuperar la instancia de SaneSpinner después de la inflación y llamar a la selección de conjuntos de esta manera:
Con esto, no se activa ningún evento y la interacción del usuario no se interrumpe. Esto redujo mucho la complejidad de mi código. Esto debería estar incluido en el stock de Android, ya que realmente es un PITA.
fuente
No hay eventos no deseados de la fase de diseño si difiere la adición del oyente hasta que el diseño finalice:
fuente
ViewTreeObserver.OnGlobalLayoutListener
versiones anteriores a J llamandoViewTreeObserver.removeGlobalOnLayoutListener
, que está en desuso y tiene un nombre similar al método que utiliza esta respuesta.Esto sucederá si realiza una selección en el código como;
En lugar del uso de la declaración anterior
Editar: Este método no funciona para Mi Android Versión Mi UI.
fuente
Obtuve una respuesta muy simple, 100% segura de que funciona:
fuente
He encontrado una solución mucho más elegante para esto. Implica contar cuántas veces se ha invocado el ArrayAdapter (en su caso "adaptador"). Digamos que tienes 1 spinner y llamas:
Declare un contador int después del método onCreate y luego dentro del método onItemSelected () ponga una condición "if" para verificar cuántas veces se ha llamado al atapter. En su caso, lo tiene llamado solo una vez, entonces:
fuente
Mi pequeña contribución es una variación de algunos de los anteriores que me ha quedado bien varias veces.
Declare una variable entera como valor predeterminado (o el último valor utilizado guardado en las preferencias). Use spinner.setSelection (myDefault) para establecer ese valor antes de que se registre el oyente. En onItemSelected, compruebe si el nuevo valor de la ruleta es igual al valor que asignó antes de ejecutar cualquier código adicional.
Esto tiene la ventaja adicional de no ejecutar código si el usuario selecciona nuevamente el mismo valor.
fuente
Después de haber tenido el mismo problema, llegué a estas soluciones usando etiquetas. La idea detrás de esto es simple: cada vez que la rueda giratoria se cambie programáticamente, asegúrese de que la etiqueta refleje la posición seleccionada. En el oyente, verifica si la posición seleccionada es igual a la etiqueta. Si lo hace, la selección de la ruleta se cambió programáticamente.
A continuación se muestra mi nueva clase "proxy spinner":
También necesitará un archivo XML con la configuración de etiqueta en su
Values
directorio. Llamé a mi archivospinner_tag.xml
, pero eso depende de usted. Se parece a esto:Ahora reemplace
en tu código con
Y haga que su controlador se vea algo así:
La función
isUiTriggered()
devolverá verdadero si y solo si el usuario ha cambiado la ruleta. Tenga en cuenta que esta función tiene un efecto secundario: establecerá la etiqueta, por lo que siempre devolverá una segunda llamada en la misma llamada de escuchafalse
.Este contenedor también resolverá el problema con la llamada del oyente durante la creación del diseño.
Diviértete, Jens.
fuente
Como nada funcionó para mí, y tengo más de 1 spinner en mi opinión (y en mi humilde opinión, tener un mapa de bool es una exageración), uso la etiqueta para contar los clics:
fuente
Muchas respuestas ya, aquí está la mía.
Extiendo
AppCompatSpinner
y agrego un métodopgmSetSelection(int pos)
que permite la configuración de selección programática sin activar una devolución de llamada de selección. He codificado esto con RxJava para que los eventos de selección se envíen a través de unObservable
.Un ejemplo de su uso, llamado
onCreateView()
en unFragment
por ejemplo:donde
setSelection()
es un método en la vista adjunta que se ve así, y que se llama tanto desde los eventos de selección del usuario a través del programaObservable
como también en otros lugares, por lo que la lógica para manejar las selecciones es común a ambos métodos de selección.fuente
Intentaría llamar
después de llamar a setAdapter (). También intente llamar antes del adaptador.
Siempre tiene la solución para ir con subclases, donde puede ajustar una bandera booleana a su método setAdapter reemplazado para omitir el evento.
fuente
La solución con una bandera booleana o un contador no me ayudó, porque durante el cambio de orientación onItemSelected () llama "sobrevoló" la bandera o el contador.
Subclase
android.widget.Spinner
e hice pequeñas adiciones. Las partes relevantes están a continuación. Esta solución funcionó para mí.fuente
Esta tampoco es una solución elegante. De hecho, es más bien Rube-Goldberg, pero parece funcionar. Me aseguro de que la ruleta se haya utilizado al menos una vez al extender el adaptador de matriz y anular su getDropDownView. En el nuevo método getDropDownView, tengo un indicador booleano configurado para mostrar que el menú desplegable se ha utilizado al menos una vez. Ignoro las llamadas al oyente hasta que se establece la bandera.
MainActivity.onCreate ():
adaptador de matriz reemplazado:
oyente modificado:
fuente
si necesita recrear la actividad sobre la marcha, por ejemplo: cambiar temas, un indicador / contador simple no funcionará
use la función onUserInteraction () para detectar la actividad del usuario,
referencia: https://stackoverflow.com/a/25070696/4772917
fuente
Lo he hecho de la manera más simple:
onCreate ();
Hecho
fuente
fuente
Esa es mi solución final y fácil de usar:
Use el valor predeterminado
setSelection(...)
para el comportamiento predeterminado o usesetSelectionWithoutInformListener(...)
para seleccionar un elemento en el control giratorio sin activar la devolución de llamada OnItemSelectedListener.fuente
Necesito usar
mSpinner
en ViewHolder, por lo que el indicadormOldPosition
se establece en la clase interna anónima.fuente
Almacenaría el índice inicial durante la creación del objeto onClickListener.
fuente
Mi solución usa
onTouchListener
pero no restringe su uso. Crea un contenedor para,onTouchListener
si es necesario, donde se configuraonItemSelectedListener
.fuente
Podría estar respondiendo demasiado tarde en la publicación, sin embargo, logré lograr esto usando la biblioteca de enlace de datos de Android Android Databinding . Creé un enlace personalizado para asegurarme de que no se llame al oyente hasta que se cambie el elemento seleccionado, por lo que incluso si el usuario selecciona la misma posición una y otra vez, el evento no se activa.
Archivo xml de diseño
app:position
es donde está pasando la posición para ser seleccionado.Encuadernación personalizada
Puede leer más sobre el enlace de datos personalizado aquí Android Custom Setter
NOTA
No olvide habilitar el enlace de datos en su archivo Gradle
Incluya sus archivos de diseño en las
<layout>
etiquetasfuente
fuente