Añadiendo preferencias de usuario


Android nos facilita la configuración de nuestros programas, al permitir añadir una lista de preferencias que el usuario podrá modificar. Por ejemplo, el usuario podrá indicar con que frecuencia la aplicación ha de sincronizarse con el servidor o si queremos que se lancen notificaciones. Las preferencias también pueden utilizarse para que tu aplicación almacene información de forma permanente. En el capítulo 9 se estudiará cómo realizar esta función

video[Tutorial Añadir preferencias en Android

Nota: A continuación se proponen una serie de ejercicios para añadir preferencias en Asteroides. Para hacerlo en Mis Lugares utiliza el tutorial:   http://www.androidcurso.com/index.php/476.

 

Ejercicio: Añadiendo preferencias a Asteroides

1.     Abre el proyecto Asteroides.

2.   Pulsa con el botón derecho sobre la carpeta res y selecciona la opción New > Android resource file.

3.   Completa los campos File name: preferenciasyResource type: XML. Se creará el fichero res/xml/preferencias.xml.

4.     Edita el fichero e introduce el siguiente código:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:key="preferencias_principal" >
    <CheckBoxPreference
        android:key="musica"
        android:title="Reproducir música"
        android:summary="Se reproduce música de fondo"/>
    <ListPreference
        android:key="graficos"
        android:title="Tipo de gráficos"
        android:summary="Se escoge la representación de gráficos"
        android:entries="@array/tiposGraficos"
        android:entryValues="@array/tiposGraficosValores"
        android:defaultValue="1"/>
    <EditTextPreference
        android:key="fragmentos"
        android:title="Número de Fragmentos"
        android:summary="En cuantos trozos se divide un asteroide"
        android:inputType="number"
        android:defaultValue="3"/>
</PreferenceScreen>

El significado de cada etiqueta y atributo se descubre facilmente si observas el resultado obtenido que se muestra a continuación. El atributo inputTypepermite configurar el tipo de teclado que se mostrará para introducir el valor. Coinciden con el atributo de EditText. Para ver los posibles valores consultar developer.android.com/reference/android/widget/TextView.html#attr_android:inputType.

 

5.     Para almacenar los valores del desplegable, has de crear el fichero /res/values/arrays.xml con el siguiente contenido.  Para ello pulsa con el botón derecho sobre la carpeta res y selecciona la opciónNew > Android resource file. Completa los camposFile name: arrays yResource type: values.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="tiposGraficos">
        <item>vectorial</item>
        <item>bitmap</item>
        <item>3D</item>
    </string-array>
    <string-array name="tiposGraficosValores">
        <item>0</item>
        <item>1</item>
        <item>2</item>
    </string-array>
</resources>

6.     Crea ahora una nueva clase Preferencias.java con el siguiente código:

public class Preferencias extends PreferenceActivity {
   @Override protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      addPreferencesFromResource(R.xml.preferencias);
   }
}

Nota sobre Java: Pulsa Alt-Intro para que automáticamente se añadan los paquetes que faltan.

NOTA: Desde el nivel de API 11, el método addPreferencesFromResource() se ha marcado como obsoleto. En lugar de usar la clase PreferenceActivity, se recomienda utilizar PreferenceFragment. Sin embargo, si estamos trabajando con un nivel mínimo de API inferior al 11 (en nuestro caso el 9), no disponemos de esta clase. Cuando un método o clase está marcado como obsoleto, se nos indica que hay otra forma más moderna de hacerlo, incluso que en un futuro es posible que deje de estar incluido en el API. Pero esto es algo que nunca ha pasado. En Android es frecuente seguir utilizando métodos marcados como obsoletos. En el siguiente apartado se explica el uso de PreferenceFragment.

NOTA: Recientemente, Google ha creado las librerías compatibilidad preference-v7 y preference-v14 donde se define PreferenceFragmentCompact una versión de PreferenceFragment compatible con versiones anteriores. El uso de PreferenceFragmentCompact se muestra en el Gran Libro de Android Avanzado.

7.     No hay que olvidar registrar toda nueva actividad en AndroidManifest.xml.

8.  Añade a MainActivity.java el método lanzarPreferencias(). Este método ha de tener el mismo código que lanzarAcercaDe()pero lanzando la actividad Preferencias. En el Layout activity_main.xml añade al botón con texto “Configurar” en el atributo onClick el valor lanzarPreferencias.

9.  Para activar la configuración desde la opción de menú añade el siguiente código en el fichero MainActivity.java en el método onOptionsItemSelected()

if  (id == R.id.action_settings) {
       lanzarPreferencias(null);
       return true;
  }

10.  Arranca la aplicación y verifica que puedes lanzar las preferencias mediante las dos alternativas.

Utilizando PreferenceFragment

Cuando tu aplicación utilice un nivel de API 11 o superior (versión 3.0), puedes utilizar PreferenceFragment en lugar de PreferenceActivity para visualizar la lista de preferencias. La ventaja de PreferenceFragment es que muestra las preferencias en un fragment en lugar de una actividad, por lo que podrás visualizar simultáneamente en pantalla un fragment con las preferencias junto a otro fragment. Esto tendría sentido en una tableta, pero no sería interesante en un móvil. En una primera toma de contacto con Android no recomendamos utilizar fragments. Si es tu caso, te recomendamos que pases al siguiente apartado. Si prefieres utilizar PreferenceFragment puedes realizar el siguiente ejercicio.

Ejercicio: Añadiendo preferencias a con PreferenceFragment.

1.     El primer paso va a ser modificar la versión mínima de API utilizada en el proyecto Asteroides. Hemos de pasar de 9 a 11. Esto va a tener una importante repercusión en la aplicación, dado que ahora solo podrá ser instalada en dispositivos Android con versión 3.0 o superior. A finales de 2015 todavía hay algunos dispositivos con versión 2.3 (5 %), donde ya no se podrá instalar la aplicación.

Para cambiar la versión mínima, desde el navegador del proyecto, abre el fichero Gradle Scripts / build.gradle (Module:app) y modifícalo eliminando el código tachado e introduciendo el subrayado:

android {
    compileSdkVersion 22
    buildToolsVersion "22.0.1"
    defaultConfig {
        applicationId "org.example.asteroides"
        minSdkVersion 9 11
        targetSdkVersion 22
        …
    }
…

2.     Ahora vamos a crear un fragment que contenga las preferencias. Para ello añade la siguiente clase en el paquete de la aplicación:

public class PreferenciasFragment extends PreferenceFragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preferencias);
    }
}

Como puedes ver el código es idéntico al utilizado en PreferenceActivity.

3.     Ahora desde la Actividad Preferencias vamos a mostrar este fragment. Para ello elimina el código tachado e introduce el subrayado:

public class Preferencias extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preferencias);
        getFragmentManager().beginTransaction()
                .replace(android.R.id.content, new PreferenciasFragment())
                .commit();
    }
}

Desde una actividad podemos visualizar un fragment en tiempo de ejecución. Para ello utilizamos el manejador de fragments de la activdad (getFragmentManager()) y comenzamos una transacción (beginTransaction()). Una transacción es una operación de insertado, borrado o reemplazo de fragments. En el ejemplo vamos a reemplazar el contenido de la actividad por un nuevo fragment de la clase PreferenciasFragment. Finalmente se llama a commit() para que se ejecute la transacción.

4.     Ejecuta la aplicación y verifica que el resultado es idéntico al anterior.

Organizando preferencias

Cuando el número de preferencias es grande, resulta interesante organizarlas de forma adecuada. Una posibilidad consiste en dividirlas en varias pantallas, de forma que cuando se seleccione una opción en la primera pantalla, se abra una nueva pantalla de preferencias. Para organizar las preferencias de esta forma, usa el siguiente esquema:

<PreferenceScreen>
  <CheckBoxPreference …/>
  <EditTextPreference …/>
  …
  <PreferenceScreen android:title=”Modo multijugador”> 
    <CheckBoxPreference …/> 
    <EditTextPreference …/>

    …
  </PreferenceScreen> 
</PreferenceScreen>

Práctica: Organizando preferencias(I)

1. Crea una nueva lista de preferencias <PreferenceScreen> dentro de la lista de preferencias del fichero res/xml/preferencias.xml.

2. Asígnale al parámetro android:title el valor “Modo multijugador”.

3. Crea tres elementos dentro de esta lista: Activar multijugador, Máximo de jugadores y Tipo de conexión. Para este ultimo han de poder escogerse los valores: Bluetooth, Wi-Fi e Internet.

Otra alternativa para organizar las preferencias consiste en agruparlas por categorías. Con esta opción se visualizarán en la misma pantalla, pero separadas por grupos. Has de seguir el siguiente esquema:

<PreferenceScreen>
  <CheckBoxPreference …/>
  <EditTextPreference …/>
  …
  <PreferenceCategory android:title=”Modo multijugador”>
    <CheckBoxPreference …/>
    <EditTextPreference …/>
.  . …
  </PreferenceCategory>
</PreferenceScreen>

A continuación se representa la forma en la que Android muestra las categorías:

Práctica: Organizando preferencias(II)

Modifica la práctica anterior para que en lugar de mostrar las propiedades en dos pantallas, las muestre en una sola, tal y como se muestra en la imagen anterior.

Como se almacenan las preferencias de usuario

Si un usuario modifica el valor de una preferencia, este quedará almacenado de forma permanente en el dispositivo. Para conseguir esta persistencia, Android almacena las preferencias seleccionadas en un fichero XML dentro de la carpeta data/data/nombre.del.paquete/files/shared_prefs, donde nombre.del.paquete ha de ser reemplazado por el paquete de la aplicación. El nombre del fichero para almacenar las preferencias de usuario ha de ser siempre nombre.del.paquete_preferences.xml. Esto significa que solo puede haber unas preferencias de usuario por aplicación. Como se estudiará en el capítulo 9, puede haber otros ficheros de preferencias; pero, a diferencia de las preferencias de usuario, no pueden ser editadas directamente por el usuario, sino que hay que acceder a ellas por código.

Ejercicio: Donde se almacenan las preferencias de usuario

Veamos donde se han almacenado las preferencias que acabamos de crear:

1.    Para navegar por el sistema de ficheros de un dispositivo con Android Studio, abre la opción Tools > Android > Android Device Monitor y selecciona la lengüeta File Explorer.

2.    Busca el siguiente fichero:  /data/data/org.examples.asteroides/shared_prefs/ org.examples.asteroide_preferences.xml

NOTA: En un dispositivo real no tendrás permiso para acceder a estos ficheros (A menos que el dispositivo haya sido rooteado).

3. Pulsa el botón del disquete para descargar el fichero en tu PC.

4. Visualiza su contenido. Tiene que ser similar a:

<map>
   <boolean name="musica"value="true" />
   <string name="graficos">1</string> 
   <string name="fragmentos">3</string>
</map>

Accediendo a los valores de las preferencias

Por supuesto, será necesario acceder a los valores de las preferencias para alterar el funcionamiento de nuestra aplicación. El siguiente ejemplo nos muestra cómo realizarlo:

public void mostrarPreferencias(){
   SharedPreferences pref =
               PreferenceManager.getDefaultSharedPreferences(this);
   String s = "música: "+ pref.getBoolean("musica",true)
            +", gráficos: " + pref.getString("graficos","?");
   Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
}

La función comienza creando el objeto pref de la clase SharedPreferences y le asigna las preferencias definidas para la aplicación. A continuación crea el String s y le asigna los valores de dos de las  preferencias. Se utilizan los métodos pref.getBoolean() y pref.getString(), que disponen de dos parámetros: el valor dekeyque queremos buscar ("musica" y "graficos") y el valor asignado por defecto en caso de no encontrar esta key.

Finalmente se visualiza el resultado utilizando la clase Toast. Los tres parámetros indicados son el contexto (nuestra actividad),  el String a mostrar y el tiempo que se estará mostrando esta información.

Ejercicio: Accediendo a los valores de las preferencias

1. Copia la función anterior en la clase Asteroides. Añade el parámetro que se muestra a continuación  mostrarPreferencias(View view).

2. Asígnala al atributo onClick del botón Jugar el método anterior.

3. Visualiza también el resto de las preferencias que hayas introducido.

Verificar valores correctos

En muchas ocasiones vas a querer limitar los valores que un usuario puede introducir en las preferencias. Por ejemplo, podría ser interesante que el valor introducido por el usuario en la preferencia número de fragmentos solo pudiera tomar valores entre 0 y 9. Para conseguir esto podemos utilizar el escuchador de evento onPreferenceChangeListener que podremos asignar a una preferencia. Veamos cómo actuar en el siguiente ejercicio:

Ejercicio: Verificar valores correctos de una preferencia

1.     Copia el siguiente código al final del método onCreate():

final EditTextPreference fragmentos = (EditTextPreference)findPreference(
                                                           "fragmentos");
fragmentos.setOnPreferenceChangeListener(
  new Preference.OnPreferenceChangeListener() {
    @Override
    public boolean onPreferenceChange(Preference preference, Object
                                                              newValue) {
       int valor;
       try {
          valor = Integer.parseInt((String)newValue);
       } catch(Exception e) {
          Toast.makeText(getActivity(), "Ha de ser un número",
                                              Toast.LENGTH_SHORT).show();
          return false;
       }
       if (valor>=0 && valor<=9) {
          fragmentos.setSummary(
                 "En cuantos trozos se divide un asteroide ("+valor+")");
          return true;
       } else {
          Toast.makeText(getActivity(), "Máximo de fragmentos 9",
                                              Toast.LENGTH_SHORT).show();
          return false;
       }
    }
  });

El código comienza obteniendo una referencia de la preferencia fragmentos, para asignarle un escuchador que será llamado cuando cambie su valor. El escuchador comienza convirtiendo el valor introducido a entero. En caso de producirse un error es por que el usuario no ha introducido un valor adecuado. En este caso, mostramos un mensaje y devolvemos false para que el valor de la preferencia no sea modificado. Si no hay error, tras verificar el rango de valores aceptables, modificamos la explicación de la preferencia para que aparezca el nuevo valor entre paréntesis y devolvemos true para aceptar este valor. Si no está en el rango, mostramos un mensaje indicando el problema y devolvemos false.

2.     Ejecuta el proyecto y verifica que funciona correctamente.

Práctica: Mostrar el valor de una preferencia

En el ejercicio anterior cuando se modifica el número de fragmentos se muestra entre paréntesis el nuevo valor introducido. El funcionamiento no es del todo correcto, cuando entramos por primera vez o cuando se cambia el teléfono de vertical a horizontal este valor no se muestra. Añade el código necesario para que este valor aparezca siempre.

 

Preguntas de repaso: menús y preferencias