Programación Android, Preferencias de usuario (SharedPreferences, PreferencesActivity y PreferenceFragment)

Share if you like...Share on Facebook0Tweet about this on TwitterShare on Google+0Share on LinkedIn0

SharedPreferences

Después de unas semanas de vacaciones volvemos a la carga con una nueva publicación sobre programación Android. En esta ocasión vamos a hablar de la posibilidad de almacenar preferencias de usuario (SharedPreferences) en nuestra aplicación, utilizando la herramienta que nos facilita Android para ello.

Para explicar el uso de esta herramienta vamos a crear una aplicación con una pantalla que contenga un TextView. El texto a mostrar, el tamaño de la fuente y el fondo del TextView los podremos configurar a partir de las preferencias del usuario, o dejar unos valores por defecto.

En primer lugar vamos a crear una Activity en nuestro proyecto que contenga un TextView, que será el que muestre los valores de las preferencias, y un Button que lanzará la pantalla de preferencias:

Main Activity
Main Activity





    <button>


Crear una pantalla de preferencias

A continuación crearemos la pantalla que contendrá las preferencias de la aplicación. Esta pantalla es un tipo especial de Activity, llamado PreferenceActivity. Para crearla vamos a hacerlo a mano, en lugar de utilizar el asistente que nos ofrece eclipse para crear nuevas Activities, ya que si lo hacemos por este medio automático se nos creará la PreferenceActivity con bastante código que no vamos a necesitar en ningún caso. De manera que en el mismo package que contiene nuestra Activity principal vamos a crear una nueva clase, llamada PrefActivity que extienda de PreferenceActivity, y sobreescribiremos el método onCreate (no olvidar declarar la Activity posteriormente en el manifiest):

/**
 * Activity que muestra y almacena las preferencias de usuario de la aplicación.
 *
 * @author Proyecto Simio proyectosimio@gmail.com
 *
 */
public class PrefActivity extends PreferenceActivity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		addPreferencesFromResource(R.xml.preferences);
	}

}

Esto funcionará sin problemas en todas las versiones, sin embargo a partir de versiones API 11 se modificó la forma de utilizar las preferencias, en lugar de utilizar una PreferenceActivity se utiliza una clase que extiende de PreferenceFragment, que contendría el mismo método onCreate que la clase PreferenceActivity que hemos creado, pero cambiando protected por public:

/**
 * Clase que muestra y almacena las preferencias de usuario de la aplicación.
 *
 * @author Proyecto Simio proyectosimio@gmail.com
 *
 */
public class PrefFragment extends PreferenceFragment {

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		addPreferencesFromResource(R.xml.preferences);
	}

}

Posteriormente creamos una Activity normal, que será la que muestre las preferencias. Para mostrar el fragment creado como contenido principal de la actividad utilizaremos un fragment manager, que sustituirá el contenido de la pantalla (android.R.id.content) por el de nuestro fragment de preferencias:

public class PrefActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Cambiamos el contenido de la Activity por nuestro propio fragment.
        getFragmentManager().beginTransaction()
                .replace(android.R.id.content, new PrefFragment())
                .commit();
    }
}

El siguiente paso es crear el recurso xml que representa la vista de nuestras preferencias, que en este caso deberá ir en la carpeta res/xml de nuestro proyecto, a diferencia del resto de layouts que se almacenan en res/layout. Este recurso xml que debemos crear será del tipo Preferences:

Crear recurso xml para PreferenceActivity
Crear recurso xml para PreferenceActivity

Llegados a este punto vamos a dejar de lado el proyecto que estamos creando, para explicar qué recursos podemos utilizar en nuestras preferencias. En primer lugar, el contenedor de nuestras preferencias será <PreferenceScreen>. Este contenedor a su vez puede incorporar directamente las preferencias que queramos utilizar, o podremos dividirlas en diferentes categorías. Para utilizar distintas categorías necesitamos añadir un <PreferenceCategory> por cada categoría que queramos definir. Para definir las preferencias podemos utilizar 4 tipos de elementos distintos:

  • CheckBoxPreference. Cuadro para habilitar/deshabilitar.
  • EditTextPreference. Permite introducir valores de texto.
  • ListPreference. Lista de valores de selección única.
  • MultiSelectListPreference. Lista de valores de selección múltiple.

Veamos estas 4 opciones en detalle:

CheckBoxPreference

Es un control equivalente al CheckBox, que permite únicamente los valores habilitado y deshabilitado. Las propiedades más relevantes de este control son:

  • android:key, valor interno de la preferencia.
  • android:title, nombre de la preferencia a mostrar.
  • android:sumary, breve descripción de la preferencia.

EditTextPreference

Es un control equivalente al EditText, que permite introducir cadenas de texto. Inicialmente muestra el nombre de la preferencia y una descripción, al pulsar sobre ella se abre un cuadro de diálogo en el que aparece un EditText en el que podremos escribir el valor de la preferencia. Las propiedades más relevantes de este control son:

  • android:key, valor interno de la preferencia.
  • android:title, nombre de la preferencia a mostrar.
  • android:sumary, breve descripción de la preferencia.
  • andr0id:dialogTitle, título que se muestra en el cuadro de diálogo del EditText.

ListPreference

Este control es equivalente a un Spinner, en el que sólo puedes seleccionar uno de los valores. Al igual que el EditTextPreference, sólo muestra el nombre de la preferencia y una descripción, al pulsar sobre la preferencia se abre un cuadro de diálogo en el que aparece el Spinner en el que podemos seleccionar el valor. Las propiedades más relevantes de este control son:

  • android:key, valor interno de la preferencia.
  • android:title, nombre de la preferencia a mostrar.
  • android:sumary, breve descripción de la preferencia.
  • android:dialogTitle, título que se muestra en el cuadro de diálogo del EditText.
  • android:entries, aquí indicaremos el array que muestra los nombres de los valores a mostrar (texto que verá el usuario al desplegar el Spinner). El array debe estar declarado como recurso en un xml.
  • android:entryValues,  aquí indicaremos el array que muestra los valores internos de las opciones. El array debe estar declarado como recurso en un xml.

Las propiedades entries y entryValues actúan como si fuera un Map, con clave/valor, en el que entries sería la clave y entryValues el valor:


  <!-- Valores a mostrar -->

        Uno
        Dos
        Tres


  <!-- Valor interno -->

        uno
        dos
        tres

MultiSelectListPreference

Esta opción sólo está para versiones Android a partir de la 3.0 Honeycomb. La forma en que se usa es igual a la de ListPreference, con las mismas propiedades. Se diferencia del ListPreference en que permite seleccionar más de un valor al mismo tiempo.

Estos cuatro controles tienen más propiedades, que se pueden ver a fondo en la documentación de Android Developers.

<PreferenceCategory>

Como hemos dicho, podemos dividir nuestras preferencias en distintas categorías utilizando <PreferenceCategory>. La forma en que se utilizan las categorías es la siguiente:

<!-- Listado de preferencias en Sección 1 -->
<!-- Listado de preferencias en Sección 2 -->
<!-- Listado de preferencias en Sección 3 -->

Ahora que sabemos qué opciones podemos utilizar en nuestra pantalla de preferencias, retomaremos el ejemplo y vamos a configurar las preferencias de la aplicación. Añadiremos un CheckBoxPreference, que indicará si queremos utilizar las preferencias en la aplicación, y dos categorías, una para el TextView, en la que pondremos el listado de colores posibles del fondo, y otra categoría para el propio texto del TextView, en la que pondremos el tamaño de fuente y el propio texto a mostrar:

<!--?xml version="1.0" encoding="utf-8"?-->








A continuación creamos los cuatro arrays que necesitamos en un nuevo archivo de recursos arrays.xml en la carpeta res/values:

<!--?xml version="1.0" encoding="utf-8"?-->



        Normal
        Mediano
        Grande


        normal
        medium
        big


        Rojo
        Gris
        Azul


        red
        gray
        blue



Con esto nuestra pantalla de preferencias quedaría así:

Pantalla de preferencias
Pantalla de preferencias
ListPreference del color de fondo
ListPreference del color de fondo
EditTextPreference del texto
EditTextPreference del texto
ListPreference para el tamaño del texto
ListPreference para el tamaño del texto

Ahora tendremos que lanzar esta Activity desde la pantalla principal de nuestra aplicación, cuando pulsemos el botón de preferencias. Esto se hace igual que si se tratara de una Activity, tanto para el caso de usar PreferenceActivity como PreferenceFragment:

public void onClick(View v) {
	Intent i = new Intent(this, PrefActivity.class);
	startActivity(i);
}

Leer las preferencias del sistema

El siguiente paso es leer los datos que tenga guardada la aplicación en las preferencias. Estos datos son persistentes, y seguirán almacenados aunque se cierre la aplicación. Android para ello utiliza un archivo xml, que almacena en la ruta /data/data/package/shared_prefs/package_preferences.xml, en el caso del ejemplo que estamos haciendo sería /data/data/com.proyectosimio.sharedpreferences/shared_prefs/com.proyectosimio.sharedpreferences_preferences.xml. Esto quiere decir que cada vez que abramos nuestra aplicación tendremos que comprobar si el usuario ha definido las preferencias, y si se desean utilizar. Para optimizar código recordemos que según el ciclo de vida de las Activities en Android, tanto si se lanza la Activity por primera vez, como si se vuelve a ella a partir de cualquier otra (cerrando la siguiente Activity) siempre se va a ejecutar el método onResume(), por lo tanto, en este caso, vamos a utilizar este método para leer las preferencias y aplicarlas. Para leer las preferencias lo haremos instanciando un objeto de la clase SharedPreferences, y para inicializarlo utilizaremos un método estático de la clase PreferenceManager que recogerá las preferencias de la aplicación:

SharedPreferences pref = PreferenceManager
				.getDefaultSharedPreferences(this);

Para recoger los valores de las preferencias, la clase SharedPreferences tiene diversos métodos. Todos los métodos reciben dos parámetros, el primero un String que se refiere a la clave de la preferencia que queremos utilizar (el valor asignado a la propiedad android:key) y un segundo parámetro que se utilizará como valor por defecto en caso de que la preferencia no esté definida. Los métodos serían:

  • getString(“key”, String)
  • getBoolean(“key”, boolean)
  • getInt(“key”, int)
  • getFloat(“key”, float)

También existe un método getAll() que devuelve un objeto de la clase Map, que contiene todos los pares clave/valor definidos en nuestras preferencias.

Lo último que nos falta por hacer en nuestro ejemplo es, en el método onResume() como dijimos, instanciar nuestro objeto SharedPreferences, y posteriormente comprobar si en las preferencias se ha definido si deseamos utilizar los valores asignados en ellas. En caso de que el usuario haya indicado que quiere utilizar las preferencias definidas leeremos el resto de valores de las preferencias, en caso contrario utilizaremos unos valores por defecto. Posteriormente los aplicaremos al TextView:

@Override
protected void onResume() {
	// Recogemos las preferencias del sistema.
	SharedPreferences pref = PreferenceManager
			.getDefaultSharedPreferences(this);

	// Variables para almacenar los parámetros que asignaremos
	// posteriormente al TextView.
	int color, textSize;
	String text;

	// Comprobamos si se desean aplicar las preferencias, en caso de no
	// estar definida la propiedad por defecto indicamos false.
	boolean usingPreferences = pref.getBoolean("cbxPreferenceOn", false);

	// Si el usuario desea aplicar las preferencias leemos el resto de
	// preferencias.
	if (usingPreferences) {
		// Recogemos el texto.
		text = pref.getString("etpTexto", "Texto por defecto.");

		// Recogemos la preferencia de color, y asignamos el color en
		// función de la preferencia. Si no se ha definido la propiedad por
		// defecto aplicamos la transpariencia.
		String prefColor = pref.getString("lpFondo", "transparent");
		if (prefColor.equals("red"))
			color = Color.RED;
		else if (prefColor.equals("gray"))
			color = Color.GRAY;
		else if (prefColor.equals("blue"))
			color = Color.BLUE;
		else
			color = Color.TRANSPARENT;

		// Recogemos la preferencia de tamaño, y asignamos el tamaño en
		// función de la preferencia. Si no se ha definido la propiedad por
		// defecto utilizaremos el tamaño normal.
		String prefTextSize = pref.getString("lpTexto", "normal");
		if (prefTextSize.equals("medium"))
			textSize = 18;
		else if (prefTextSize.equals("big"))
			textSize = 22;
		else
			textSize = 14;
	} else {
		// Si no se desean aplicar las preferencias damos valores por
		// defecto.
		text = "Texto por defecto.";
		color = Color.TRANSPARENT;
		textSize = 18;
	}

	// Aplicamos los parámetros al TextView.
	tvTexto.setText(text);
	tvTexto.setBackgroundColor(color);
	tvTexto.setTextSize(textSize);

	super.onResume();
}

Descargas

Puedes ver el resultado del ejemplo en tu móvil descargando nuestra aplicación desde Google Play.

Puedes descargar el proyecto completo aquí.

Share if you like...Share on Facebook0Tweet about this on TwitterShare on Google+0Share on LinkedIn0
The following two tabs change content below.
Reborn as IT Developer. Desarrollador Android y fundador de Proyecto Simio. "En realidad, yo no puedo enseñar nada a nadie, sólo puedo hacerles pensar." - Sócrates.

7 thoughts on “Programación Android, Preferencias de usuario (SharedPreferences, PreferencesActivity y PreferenceFragment)

  1. Hola aparte de lo que esta aquí lo cual me resulto genial, muchas gracias :) , quería saber si puedo configurar fecha y hora y de que modo lo haría .. gracias

  2. Como veo la pantalla de preferencias que se crea, es como un listview. ¿Se podra usar como el listview, osea utilizar el evento onitemclicklistener, para al dar click en alguna opcion, por ejemplo me llame a alguna actividad?

    1. Hasta donde yo sé, no. Pero tampoco me he puesto a investigar este asunto a fondo nunca porque suelo utilizar los componentes tal y como la filosofía de Android indica, ya que al final es lo que el usuario espera que suceda en la aplicación.

      Un saludo.

    1. Pues en el inicio de la aplicación puedes preguntar por algún valor de las preferencias que haya tenido que configurar, y si no tiene nada establecido ya sabes que no las ha configurado.

      A partir de ahí sólo tienes que crear la lógica de tu aplicación, como preguntar si se desean configurar las preferencias y si el usuario selecciona que si mostrar la ventana y si dice que no cerrar la app… O lo que quieras que haga la aplicación.

      El método onResume puede ser la mejor opción para controlar las preferencias, para poder ver así cuando el usuario cierre las preferencias si realmente las ha configurado o no.

      Un saludo.

Deja un comentario