2010-04-14 11 views
22

El controlador de eventos OnItemSelectedListener se llama cuando una selección de spinner se cambia programáticamente y cuando un usuario hace clic físicamente en el control del control giratorio. ¿Es posible determinar si un evento fue desencadenado por una selección de usuario de alguna manera?Android Spinner selection

¿O hay otra forma de manejar las selecciones de usuarios de centrifugadoras?

+0

Esto funcionó para mí ajuste seleccionado con el ejecutable http://stackoverflow.com/a/13528576/1104279 – Gil

Respuesta

49

Para solucionar el problema, debe recordar la última posición seleccionada. Luego, dentro de tu oyente spinner compara la última posición seleccionada con la nueva. Si son diferentes, entonces procese el evento y también actualice la última posición seleccionada con el nuevo valor de posición; de lo contrario, omita el procesamiento del evento.

Si en alguna parte dentro del código va a cambiar programáticamente la posición seleccionada del girador y no desea que el oyente procese el evento, simplemente restablezca la última posición seleccionada a la que va a establecer.

Sí, Spinner en Android es doloroso. Incluso diría que el dolor comienza por su nombre: "Spinner". ¿No es un poco engañoso? :) Por lo que estamos hablando, también debes saber que hay un error: Spinner puede no restaurar (no siempre) su estado (en la rotación del dispositivo), así que asegúrate de manejar el estado de Spinner manualmente.

+0

¿Qué sucede si el usuario desea seleccionar el artículo en la posición 0? Por lo que entiendo su solución, sería ignorado –

+0

@ NicolásCarrasco, fue hace 6 años ... Simplemente no puedo recordar el contexto, pero definitivamente no tuve problemas con la solución. Probablemente considere usar -1 como valor inicial para la última posición seleccionada. –

+0

Si comienza el valor como -1, cuando el Spinner está desplegado se llamará al Listener con position = 0 como parámetro. Por supuesto -1! = 0, por lo que el evento se dispararía y el problema mencionado en esta pregunta aún no se ha resuelto. No quiero dar a entender que tu respuesta es incorrecta, pero no está completa –

2

En el pasado he hecho cosas como esta para distinguir

internal++; // 'internal' is an integer field initialized to 0 
textBox.setValue("...."); // listener should not act on this internal setting 
internal--; 

Luego, en escucha de textBox

if (internal == 0) { 
    // ... Act on user change action 
} 

utilizo ++ y - en lugar de establecer un valor booleano para 'verdadero 'para que no haya preocupaciones cuando los métodos anidan otros métodos que también pueden configurar el indicador de cambio interno.

+6

No funciona en la ruleta, porque El método onItemSelected se llama asíncronamente cuando se despacha el evento correspondiente. – Arutha

20

difícil de creer que un año y medio después, el problema sigue existiendo y sigue a aturdir la gente ...

pensé en compartir la solución que se me ocurrió después de leer el post más útil de Arhimed (gracias , ¡y estoy de acuerdo con que los hilanderos sean dolorosos!). Lo que he estado haciendo para evitar estos falsos positivos es el uso de un simple contenedor de clase:

import android.util.Log; 
import android.view.View; 
import android.widget.AdapterView; 
import android.widget.AdapterView.OnItemSelectedListener; 

public class OnItemSelectedListenerWrapper implements OnItemSelectedListener { 

    private int lastPosition; 
    private OnItemSelectedListener listener; 

    public OnItemSelectedListenerWrapper(OnItemSelectedListener aListener) { 
     lastPosition = 0; 
     listener = aListener; 
    } 

    @Override 
    public void onItemSelected(AdapterView<?> aParentView, View aView, int aPosition, long anId) { 
     if (lastPosition == aPosition) { 
      Log.d(getClass().getName(), "Ignoring onItemSelected for same position: " + aPosition); 
     } else { 
      Log.d(getClass().getName(), "Passing on onItemSelected for different position: " + aPosition); 
      listener.onItemSelected(aParentView, aView, aPosition, anId); 
     } 
     lastPosition = aPosition; 
    } 

    @Override 
    public void onNothingSelected(AdapterView<?> aParentView) { 
     listener.onNothingSelected(aParentView); 
    } 
} 

único que hace es artículo trampa eventos seleccionados para la misma posición que ya fue seleccionado (por ejemplo, la selección inicial en marcha automáticamente posición 0), y pasar otros eventos al oyente envuelto. Para usarlo, todo lo que tiene que hacer es modificar la línea en su código que llama al oyente para incluir la envoltura (y añadir el corchete de cierre, por supuesto), así que en vez de, por ejemplo:

mySpinner.setOnItemSelectedListener(new OnItemSelectedListener() { 
    ... 
}); 

que había tener esto:

mySpinner.setOnItemSelectedListener(new OnItemSelectedListenerWrapper(new OnItemSelectedListener() { 
    ... 
})); 

Obviamente una vez que hemos probado, podría deshacerse del registro de llamadas, y podría añadir la capacidad de restablecer la última posición si es necesario (que tendría que mantener una referencia a la instancia, por supuesto, en lugar de declarar sobre la marcha) como dijo Arhimed.

Esperamos que esto pueda ayudar a alguien de ser conducido loco por este extraño comportamiento ;-)

1

También busqué una buena solución en Internet, pero no encontré ninguna que satisfizo mis necesidades. Así que escribí esta extensión en la clase Spinner para que pueda establecer un OnItemClickListener simple, que tiene el mismo comportamiento que un ListView.

Solo cuando un elemento se 'selecciona', se llama al onItemClickListener.

Diviértete con ello!

public class MySpinner extends Spinner 
    { 
     private OnItemClickListener onItemClickListener; 


     public MySpinner(Context context) 
     { 
      super(context); 
     } 

     public MySpinner(Context context, AttributeSet attrs) 
     { 
      super(context, attrs); 
     } 

     public MySpinner(Context context, AttributeSet attrs, int defStyle) 
     { 
      super(context, attrs, defStyle); 
     } 

     @Override 
     public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener inOnItemClickListener) 
     { 
      this.onItemClickListener = inOnItemClickListener; 
     } 

     @Override 
     public void onClick(DialogInterface dialog, int which) 
     { 
      super.onClick(dialog, which); 

      if (this.onItemClickListener != null) 
      { 
       this.onItemClickListener.onItemClick(this, this.getSelectedView(), which, this.getSelectedItemId()); 
      } 
     } 
    } 
0

Hice algunos registros y descubrí que solo se llama al inicializar, lo que es molesto. No puedo ver la necesidad de todo este código, acabo de crear una variable de instancia que se inicializó en un valor de protección, y luego lo configuré después de que se había llamado al método la primera vez.

Inicié sesión cuando se llamaba al método onItemSelected, de lo contrario solo se llamaba una vez.

Tuve un problema donde creaba dos de algo y me di cuenta de que era porque estaba llamando a add() en mi adaptador personalizado, que ya tenía una referencia a la lista a la que hacía referencia y que había agregado fuera del adaptador. Después de darme cuenta de esto y eliminar el método add, el problema desapareció.

¿Están seguros de que necesitan todo este código?

1

Solo para ampliar la publicación de aaamos anterior, ya que no tengo los 50 puntos de repetición para comentar, estoy creando una nueva respuesta aquí.

Básicamente, su código funciona para el caso en la selección inicial Spinner es 0. Sin embargo, para generalizarlo, I modificado su código de la siguiente manera:

@Override 
public void setOnItemSelectedListener(final OnItemSelectedListener listener) 
{ 
    if (listener != null) 
     super.setOnItemSelectedListener(new OnItemSelectedListener() 
     { 
      private static final int NO_POSITION = -1; 

      private int lastPosition = NO_POSITION; 


      @Override 
      public void onItemSelected(AdapterView<?> parent, View view, int position, long id) 
      { 
       if ((lastPosition != NO_POSITION) && (lastPosition != position)) 
        listener.onItemSelected(parent, view, position, id); 

       lastPosition = position; 
      } 


      @Override 
      public void onNothingSelected(AdapterView<?> parent) 
      { 
       listener.onNothingSelected(parent); 
      } 
     }); 
    else 
     super.setOnItemSelectedListener(null); 
} 

Básicamente, este código no hará caso de la primera cocción de onItemSelected(), y luego todas las llamadas subsiguientes de "misma posición".

Por supuesto, el requisito es que la selección se establece mediante programación, pero que debería ser el caso de todas formas, si la posición por defecto no es 0.

2

tuve esta situación últimamente al utilizar hiladores y el internet no me paré' t se le ocurrió una solución adecuada.

Mi escenario de aplicación:

spinners X (dinámicamente, 2 para cada CPU, min & max) para configurar & ver la CPU-frecuencia. Se llenan cuando se inicia la aplicación y también obtienen la frecuencia máxima/mínima actual del conjunto de CPU. Se ejecuta un hilo en el fondo y comprueba los cambios cada segundo y actualiza los giradores en consecuencia. Si el usuario establece una nueva frecuencia dentro de la ruleta, se establece una nueva frecuencia.

El problema era que el hilo accedía a setSelection para actualizar la frecuencia actual que a su vez llamaba a mi oyente y no tenía manera de saber si era el usuario o el hilo el que cambiaba el valor. Si era el hilo, no quería que se llamara al oyente, ya que no habría habido necesidad de cambiar la frecuencia.

me ocurrió una solución que se adapte a mis necesidades perfectamente y trabaja alrededor del oyente en su llamada :) (y creo que esta solución le da el máximo control)

extendí Spinner:

import android.content.Context; 
import android.widget.Spinner; 

public class MySpinner extends Spinner { 
    private boolean call_listener = true; 

    public MySpinner(Context context) { 
     super(context); 
    } 

    public boolean getCallListener() { 
     return call_listener; 
    } 

    public void setCallListener(boolean b) { 
     call_listener = b; 
    } 

    @Override 
    public void setSelection(int position, boolean lswitch) { 
     super.setSelection(position); 
     call_listener = lswitch; 
    } 
} 

y creado mi propia OnItemSelectedListener:

import android.util.Log; 
import android.view.View; 
import android.widget.AdapterView; 
import android.widget.AdapterView.OnItemSelectedListener; 

public class SpinnerOnItemSelectedListener implements OnItemSelectedListener { 
     public void onItemSelected(AdapterView<?> parent, View view, int pos,long id) { 
      MySpinner spin = (MySpinner) parent.findViewById(parent.getId()); 
      if (!spin.getCallListener()) { 
       Log.w("yourapptaghere", "Machine call!"); 
       spin.setCallListener(true); 
      } else { 
       Log.w("yourapptaghere", "UserCall!"); 
      } 
     } 

     @Override 
     public void onNothingSelected(AdapterView<?> arg0) { 
     // TODO Auto-generated method stub 
     } 
} 

Si ahora crear un MySpinner puede utilizar esto para establecer la selección:

setSelection(position, callListener);

Donde callListener es verdadero o falso. True llamará al oyente y es el predeterminado, por lo que las interacciones del usuario se identifican, falso también llamará al oyente pero utiliza el código que desea para este caso especial, ejempli gratia en mi caso: Nada.

espero que alguien encuentra este útil y está a salvo de un largo viaje para mirar si algo como esto ya existe :)

0

Sé que esto es bastante tarde, pero se me ocurrió una solución muy simple a este . Se basa en la respuesta de Arhimed, es exactamente lo mismo. Es muy fácil de implementar también. Consulte la respuesta aceptada:

Undesired onItemSelected calls