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.