jueves, 15 de septiembre de 2011

Elementos básicos del interface de usuario – Spinner (II) - Basic elements of the user interface


Para controlar cuando un elemento del Spinner es seleccionado existe el método onItemSelected(), semejante al onClick() que vimos para los RadioButton o los Button. Por cierto, entre los atributos XML de la definición de un Spinner podeis comprobar que existe un onClick en el que teóricamente podríamos definir el método a ejecutar pero da un error de ejecución (al menos yo no he sido capaz de hacerlo funcionar).

Para implementar el método onItemSelected hay que definir un listener y asociarlo al Spinner de la siguiente forma:

public class PruebaSpinner2Activity extends Activity {
     
      String[] paises = { "Ninguno", "Alemania", "Argentina",
                  "Brasil", "España", "Francia",
                  "Italia", "Mexico", "Peru"};

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        Spinner spPaises = (Spinner) findViewById (R.id.spinner);
        // Defino el adaptador para el Spinner
        ArrayAdapter<String> aa_paises = new ArrayAdapter<String>
                (this, android.R.layout.simple_spinner_item, paises);
        // Asigno el adaptador al Spinner       
        spPaises.setAdapter(aa_paises);
        // Asigno el listener al Spinner
        spPaises.setOnItemSelectedListener(new SpinnerListener());
    }

    // Denificion del listener
    public class SpinnerListener implements OnItemSelectedListener {

      // Metodo onItemSelected en el que indicamos lo que queremos hacer
      // cuando sea seleccionado un elemento del Spinner
        public void onItemSelected(AdapterView<?> parent,
            View view, int pos, long id) {
                TextView resultado = (TextView) findViewById (R.id.resultado);
                // Cargo en un TextView el contenido del elemento seleccionado.
                resultado.setText("Ha seleccionado: "+
                                  parent.getItemAtPosition(pos). toString());
        }
        public void onNothingSelected(AdapterView<?> parent) {
          // Do nothing.
        }
    }
}

En la definición del listener se incluyen dos métodos, uno en el que indicamos lo que queremos hacer cuando sea seleccionado un elemento del Spinner (onItemSelected) y otro para cuando no haya ningún elemento seleccionado porque el adaptador esté vacío (onNothingSelected).

El resultado:
 
Cargando Spinners dinámicamente

Puede ser que se nos dé el caso de que el contenido de un Spinner dependa de la selección que se haga en otro. En el ejemplo de paises que estamos siguiendo, imaginemos que tenemos un segundo Spinner con las principales ciudades del país seleccionado. Logicamente si cambiamos el pais, debemos variar el contenido del segundo Spinner.

Veamos cómo podemos solucionarlo:

Fichero /res/values/main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
       <TextView 
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:textStyle="bold"
          android:text="@string/tv_pais">
       </TextView>
       <Spinner android:id="@+id/paises"
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
          android:prompt="@string/sp_paises"
          android:entries="@array/arrayPaises">
       </Spinner>
       <TextView 
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:textStyle="bold"
          android:text="@string/tv_ciudad">
       </TextView>
       <Spinner android:id="@+id/ciudades"
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
          android:prompt="@string/sp_ciudades">
       </Spinner>
</LinearLayout>

En el fichero xml definimos los dos Spinner pero sólo utilizo el atributo android:entries en el primero cargando el arrayPaises que es fijo. El contenido del Spinner ciudades se cargará dinámicamente en función de la selcción del país.

Fichero /res/values/arrays.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="arrayPaises">
        <item>Ninguno</item>
        <item>España</item>
        <item>Francia</item>
        <item>Mexico</item>
    </string-array>
    <string-array name="arrayEspana">
        <item>Alicante</item>
        <item>Barcelona</item>
        <item>Madrid</item>
        <item>Sevilla</item>
    </string-array>   
    <string-array name="arrayFrancia">
        <item>Burdeos</item>
        <item>Paris</item>
        <item>Lyon</item>
        <item>Marsella</item>
    </string-array>
    <string-array name="arrayMexico">
        <item>Ciudad de Mexico</item>
        <item>Guadalajara</item>
        <item>Monterrey</item>
        <item>Acapulco</item>
    </string-array>
    <string-array name="arrayDefecto">
        <item>--</item>
    </string-array>   
</resources>

En nuestro código Java:

public class PruebaSpinnerActivity extends Activity {
     
      Spinner spCiudades;
      ArrayAdapter<CharSequence> aa_espana;
      ArrayAdapter<CharSequence> aa_francia;
      ArrayAdapter<CharSequence> aa_mexico;
      ArrayAdapter<CharSequence> aa_default;
     
      /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        Spinner spPaises = (Spinner) findViewById(R.id.paises);
        // Asigno el listener al Spinner de paises
        spPaises.setOnItemSelectedListener(new SpinnerListener());
    } 
    public class SpinnerListener implements OnItemSelectedListener {

        public void onItemSelected(AdapterView<?> parent,
            View view, int pos, long id) {
            // Lamo a un método para cargar el Spinner de ciudades
            // pasandole la posicion del elemento seleccionado en
            // el Spinner de paises
            cargaSpinnerCiudad(parent.getSelectedItemPosition());
        }
        public void onNothingSelected(AdapterView<?> parent) {
          // Do nothing.
        }
    } 
    // Defino los adaptadores para cada pais y asigno el que corresponde
    private void cargaSpinnerCiudad(int pais){

      spCiudades = (Spinner) findViewById(R.id.ciudades);
      aa_espana = ArrayAdapter.createFromResource
                      (this, R.array.arrayEspana,
                      android.R.layout.simple_spinner_item);
      aa_francia = ArrayAdapter.createFromResource
                       (this, R.array.arrayFrancia,
                       android.R.layout.simple_spinner_item);
      aa_mexico = ArrayAdapter.createFromResource
                      (this, R.array.arrayMexico,
                      android.R.layout.simple_spinner_item);
      aa_default = ArrayAdapter.createFromResource
                       (this, R.array.arrayDefecto,
                       android.R.layout.simple_spinner_item);         

      switch (pais) {
            case 1: spCiudades.setAdapter(aa_espana);
                    break;
            case 2: spCiudades.setAdapter(aa_francia);
                    break;
            case 3: spCiudades.setAdapter(aa_mexico);
                      break;
            default: spCiudades.setAdapter(aa_default);
                      break;                
      }
   }   
}

He definido un adaptador para cada uno de los paises mas uno por defecto por si no seleccionan ninguno.
Asigno un listener al Spinner de paises y cada vez que se selecciona un elemento llamo al método cargaSpinnerCiudad(pais) pasandole como parámetro la posición del elemento seleccionado en el Spinner de paises con el método getSelectedItemPosition().
Una vez conocido el pais asigno al Spiner de ciudades el adaptador correspondiente.

El resultado:
 

Inicialmente se carga el Spinner de paises con el arrayPaises y el de ciudades con el arrayDefault.

 
Una vez seleccionado un pais se actualiza el Spinner de ciudades automáticamente.

Espero que os sea de utilidad.
Hasta la próxima.


9 comentarios:

  1. fernandofv@gmail.com24 de mayo de 2012, 23:36

    Buenas tardes, me aparecen una seria de errores al seguirlo paso a paso, tendras el proyecto (codigo) que me puedes facilitar?

    ResponderEliminar
    Respuestas
    1. Hola, no suelo guardar el código de los ejemplos del blog, de todas maneras indícame alguno de los errores que te aparecen y procuraré indicarte la solución.
      Saludos.

      Eliminar
  2. Buenas noches, excelente tu entrada.. me ayudo muchisimo, pero sigo sin poder hacer algo, tengo 4 spinner y necesito condicionarlos, lo mas parecido a tu ejmplo seria que yo necesito PAIS, CIUDAD, ESTADOS, MUNICIPIOS Y PARROQUIAS, obviamente la seleccion de uno condiciona al siguiente pero no se como hacerlo de ciudad a estado, podrias darme alguna orientacion por favor.

    ResponderEliminar
    Respuestas
    1. Hola Kndy, supongamos que queremos añadir un nuevo nivel de Spinner dinamico, es decir, Pais, Ciudad y Estado. Basicamente tendrias que definir un array con los elementos de cada estado en el fichero arrays.xml, despues definir un ArrayAdapter por cada uno de ellos y, por ultimo un Listener para el spinner de ciudades.

      La clave está en que el método llamado por el listener de ciudades debe recibir dos parámetros, el pais y la ciudad seleccionada y con ellos podremos asignar el adapter correspondiente al estado.

      Espero haberte ayudado.
      Un saludo y gracias por colaborar.

      Eliminar
    2. Hola que tal, me sirve el ejemplo pero, mi caso esta asi, necesito esto de dinamica entre spinner, pero en mi caso desde una base de datos, que al seleccionar el pais, se actualise igual a como en tu ejemplo pero todo desde una base de datos SQLITE teniendo en tablas diferentes cada dato en una tabla los paises en otra los estados..., saludos, espero me puedas ayudar...

      Eliminar
  3. Muchas gracias por compartir tu conocimiento, tengo la siguiente duda, en el case de que quiera guardar el valor del spinner para que cuando vuelva a ingresar me muestre los valores guardados, agradezco si me puedes indicar como se debería hacer. gracias

    ResponderEliminar
  4. excelente!!eres un crack

    ResponderEliminar
  5. como hacer que lo que seleccione en el spinner se guarde en una base de datos

    ResponderEliminar