Inicio Android Preferencias en Android II: PreferenceActivity

Preferencias en Android II: PreferenceActivity

por sgoliver

Ya hemos visto durante el curso algún artículo dedicado a las preferencias compartidas (shared preferences), un mecanismo que nos permite gestionar fácilmente las opciones de una aplicación permitiéndonos guardarlas en XML de una forma transparente para el programador. En aquel momento sólo vimos cómo hacer uso de ellas mediante código, es decir, creando nosotros mismos los objetos necesarios (SharedPreferences) y añadiendo, modificando y/o recuperando «a mano» los valores de las opciones a través de los métodos correspondientes (getString(), putString(), …). Sin embargo, ya avisamos de que Android ofrece una forma alternativa de definir mediante XML un conjunto de opciones para una aplicación y crear por nosotros las pantallas necesarias para permitir al usuario modificarlas a su antojo. A esto dedicaremos este segundo artículo sobre preferencias.

Si nos fijamos en cualquier pantalla de preferencias estandar de Android veremos que todas comparten una interfaz comun, similar por ejemplo a las que se muestran en las imágenes siguientes para Android 2.x y 4.x respectivamente:

ejemplo-prefactivity-android2  ejemplo-prefactivity-android4

Si atendemos por ejemplo a la primera imagen vemos cómo las diferentes opciones se organizan dentro de la pantalla de opciones en varias categoríasGeneral Settings» y «Slideshow Settings«). Dentro de cada categoría pueden aparecer varias opciones de diversos tipos, como por ejemplo de tipo checkbox («Confirm deletions«) o de tipo lista de selección («Display size«). He resaltado las palabras «pantalla de opciones», «categorías», y «tipos de opción» porque serán estos los tres elementos principales con los que vamos a definir el conjunto de opciones o preferencias de nuestra aplicación. Empecemos.

Como hemos indicado, nuestra pantalla de opciones la vamos a definir mediante un XML, de forma similar a como definimos cualquier layout, aunque en este caso deberemos colocarlo en la carpeta /res/xml. El contenedor principal de nuestra pantalla de preferencias será el elemento <PreferenceScreen>. Este elemento representará a la pantalla de opciones en sí, dentro de la cual incluiremos el resto de elementos. Dentro de éste podremos incluir nuestra lista de opciones organizadas por categorías, que se representarán mediante el elemento <PreferenceCategory> al que daremos un texto descriptivo utilizando su atributo android:title. Dentro de cada categoría podremos añadir cualquier número de opciones, las cuales pueden ser de distintos tipos, entre los que destacan:

  • CheckBoxPreference. Marca seleccionable.
  • EditTextPreference. Cadena simple de texto.
  • ListPreference. Lista de valores seleccionables (exclusiva).
  • MultiSelectListPreference. Lista de valores seleccionables (múltiple).

Cada uno de estos tipos de preferencia requiere la definición de diferentes atributos, que iremos viendo en los siguientes apartados.

CheckBoxPreference

Representa un tipo de opción que sólo puede tomar dos valores distintos: activada o desactivada. Es el equivalente a un control de tipo checkbox. En este caso tan sólo tendremos que especificar los atributos: nombre interno de la opción (android:key), texto a mostrar (android:title) y descripción de la opción (android:summary). Veamos un ejemplo:

<CheckBoxPreference
    android:key="opcion1"
    android:title="Preferencia 1"
    android:summary="Descripción de la preferencia 1" />

EditTextPreference

Representa un tipo de opción que puede contener como valor una cadena de texto. Al pulsar sobre una opción de este tipo se mostrará un cuadro de diálogo sencillo que solicitará al usuario el texto a almacenar. Para este tipo, además de los tres atributos comunes a todas las opciones (key, title y summary) también tendremos que indicar el texto a mostrar en el cuadro de diálogo, mediante el atributo android:dialogTitle. Un ejemplo sería el siguiente:

<EditTextPreference
   android:key="opcion2"
   android:title="Preferencia 2"
   android:summary="Descripción de la preferencia 2"
   android:dialogTitle="Introduce valor" />

ListPreference

Representa un tipo de opción que puede tomar como valor un elemento, y sólo uno, seleccionado por el usuario entre una lista de valores predefinida. Al pulsar sobre una opción de este tipo se mostrará la lista de valores posibles y el usuario podrá seleccionar uno de ellos. Y en este caso seguimos añadiendo atributos. Además de los cuatro ya comentados (key, title, summary y dialogTitle) tendremos que añadir dos más, uno de ellos indicando la lista de valores a visualizar en la lista y el otro indicando los valores internos que utilizaremos para cada uno de los valores de la lista anterior (Ejemplo: al usuario podemos mostrar una lista con los valores «Español» y «Francés», pero internamente almacenarlos como «ESP» y «FRA»).

Estas listas de valores las definiremos también como ficheros XML dentro de la carpeta /res/xml. Definiremos para ello los recursos de tipos <string-array> necesarios, en este caso dos, uno para la lista de valores visibles y otro para la lista de valores internos, cada uno de ellos con su ID único correspondiente. Veamos cómo quedarían dos listas de ejemplo, en un fichero llamado «codigospaises.xml«:

<?xml version="1.0" encoding="utf-8" ?>
<resources>
   <string-array name="pais">
      <item>España</item>
      <item>Francia</item>
      <item>Alemania</item>
   </string-array>
   <string-array name="codigopais">
      <item>ESP</item>
      <item>FRA</item>
      <item>ALE</item>
   </string-array>
</resources>

En la preferencia utilizaremos los atributos android:entries y android:entryValues para hacer referencia a estas listas, como vemos en el ejemplo siguiente:

<ListPreference
   android:key="opcion3"
   android:title="Preferencia 3"
   android:summary="Descripción de la preferencia 3"
   android:dialogTitle="Indicar Pais"
   android:entries="@array/pais"
   android:entryValues="@array/codigopais" />

MultiSelectListPreference

[A partir de Android 3.0.x / Honeycomb] Las opciones de este tipo son muy similares a las ListPreference, con la diferencia de que el usuario puede seleccionar varias de las opciones de la lista de posibles valores. Los atributos a asignar son por tanto los mismos que para el tipo anterior.

<MultiSelectListPreference
   android:key="opcion4"
   android:title="Preferencia 4"
   android:summary="Descripción de la preferencia 4"
   android:dialogTitle="Indicar Pais"
   android:entries="@array/pais"
   android:entryValues="@array/codigopais" />

Como ejemplo completo, veamos cómo quedaría definida una pantalla de opciones con las 3 primeras opciones comentadas (ya que probaré con Android 2.2), divididas en 2 categorías llamadas por simplicidad «Categoría 1» y «Categoría 2». Llamaremos al fichero «opciones.xml».

<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory android:title="Categoría 1">
        <CheckBoxPreference
            android:key="opcion1"
            android:title="Preferencia 1"
            android:summary="Descripción de la preferencia 1" />
        <EditTextPreference
            android:key="opcion2"
            android:title="Preferencia 2"
            android:summary="Descripción de la preferencia 2"
            android:dialogTitle="Introduce valor" />
    </PreferenceCategory>
    <PreferenceCategory android:title="Categoría 2">
        <ListPreference
            android:key="opcion3"
            android:title="Preferencia 3"
            android:summary="Descripción de la preferencia 3"
            android:dialogTitle="Indicar Pais"
            android:entries="@array/pais"
            android:entryValues="@array/codigopais" />
    </PreferenceCategory>
</PreferenceScreen>

Ya tenemos definida la estructura de nuestra pantalla de opciones, pero aún nos queda un paso más para poder hacer uso de ella desde nuestra aplicación. Además de la definición XML de la lista de opciones, debemos implementar una nueva actividad, que será a la que hagamos referencia cuando queramos mostrar nuestra pantalla de opciones y la que se encargará internamente de gestionar todas las opciones, guardarlas, modificarlas, etc, a partir de nuestra definición XML.

Android nos facilita las cosas ofreciéndonos una clase de la que podemos derivar facilmente la nuestra propia y que hace casi todo el trabajo por nosotros. Esta clase se llama PreferenceActivity. Tan sólo deberemos crear una nueva actividad (yo la he llamado OpcionesActivity) que extienda a esta clase, e implementar su evento onCreate() para añadir una llamada al método addPreferencesFromResource(), mediante el que indicaremos el fichero XML en el que hemos definido la pantalla de opciones. Lo vemos mejor directamente en el código:

public class OpcionesActivity extends PreferenceActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        addPreferencesFromResource(R.xml.opciones);
    }
}

Así de sencillo, nuestra nueva actividad, al extender a PreferenceActivity, se encargará por nosotros de crear la interfaz gráfica de nuestra lista de opciones según hemos la definido en el XML y se preocupará por nosotros de mostrar, modificar y guardar las opciones cuando sea necesario tras la acción del usuario.

Aunque esto continúa funcionando sin problemas en versiones recientes de Android, la API 11 trajo consigo una nueva forma de definir las pantallas de preferencias haciendo uso de fragments. Para ello, basta simplemente con definir la clase java del fragment, que deberá extender de PreferenceFragment y añadir a su método onCreate() una llamada a addPreferencesFromResource() igual que ya hemos visto antes.

public static class OpcionesFragment extends PreferenceFragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        addPreferencesFromResource(R.xml.opciones);
    }
}

Hecho esto ya no será necesario que la clase de nuestra pantalla de preferencias extienda de PreferenceActivity, sino que podrá ser una actividad normal. Para mostrar el fragment creado como contenido principal de la actividad utilizaríamos el fragment manager para sustituir el contenido de la pantalla (android.R.id.content) por el de nuestro fragment de preferencias recién definido:

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

        getFragmentManager().beginTransaction()
                .replace(android.R.id.content, new OpcionesFragment())
                .commit();
    }
}

Sea cual se la opción elegida para definir la pantalla de preferencias, el siguiente paso será añadir esta actividad al fichero AndroidManifest.xml, al igual que cualquier otra actividad que utilicemos en la aplicación.

<activity android:name=".OpcionesActivity"
	android:label="@string/app_name">
</activity>

Ya sólo nos queda añadir a nuestra aplicación algún mecanismo para mostrar la pantalla de preferencias. Esta opción suele estar en un menú (para Android 2.x) o en el menú de overflow de la action bar (para Android 3 o superior), pero por simplificar el ejemplo vamos a añadir simplemente un botón (btnPreferencias) que abra la ventana de preferencias.

Al pulsar este botón llamaremos a la ventana de preferencias mediante el método startActivity(), como ya hemos visto en alguna ocasión, al que pasaremos como parámetros el contexto de la aplicación (nos vale con nuestra actividad principal) y la clase de la ventana de preferencias (OpcionesActivity.class).

btnPreferencias = (Button)findViewById(R.id.BtnPreferencias);

btnPreferencias.setOnClickListener(new OnClickListener() {
	@Override
	public void onClick(View v) {
		startActivity(new Intent(MainActivity.this,
			                 OpcionesActivity.class));
	}
});

Y esto es todo, ya sólo nos queda ejecutar la aplicación en el emulador y pulsar el botón de preferencias para mostrar nuestra nueva pantalla de opciones. Debe quedar como muestran las imágenes siguientes para Android 2.x y 4.x respectivamente:

demo-preferenceactivity-android-2  demo-preferenceactivity-android-4

La primera opción podemos marcarla o desmarcarla directamente pulsando sobre la check de su derecha. La segunda, de tipo texto, nos mostrará al pulsarla un pequeño formulario para solicitar el valor de la opción.

text-preference-android

Por último, la opción 3 de tipo lista, nos mostrará una ventana emergente con la lista de valores posibles, donde podremos seleccionar sólo uno de ellos.

list-preference-android

Una vez establecidos los valores de las preferencias podemos salir de la ventana de opciones simplemente pulsando el botón Atrás del dispositivo o del emulador. Nuestra actividad OpcionesActivity se habrá ocupado por nosotros de guardar correctamente los valores de las opciones haciendo uso de la API de preferencias compartidas (Shared Preferences). Y para comprobarlo vamos a añadir otro botón (btnObtenerOpciones) a la aplicación de ejemplo que recupere el valor actual de las 3 preferencias y los escriba en el log de la aplicación.

La forma de acceder a las preferencias compartidas de la aplicación ya la vimos en el artículo anterior sobre este tema. Obtenemos la lista de preferencias mediante el método getDefaultSharedPreferences() y posteriormente utilizamos los distintos métodos get() para recuperar el valor de cada opción dependiendo de su tipo.

btnObtenerPreferencias.setOnClickListener(new OnClickListener() {
	@Override
	public void onClick(View v) {
		SharedPreferences pref =
			PreferenceManager.getDefaultSharedPreferences(
				AndroidPrefScreensActivity.this);

		Log.i("", "Opción 1: " + pref.getBoolean("opcion1", false));
		Log.i("", "Opción 2: " + pref.getString("opcion2", ""));
		Log.i("", "Opción 3: " + pref.getString("opcion3", ""));
	}
});

Si ejecutamos ahora la aplicación, establecemos las preferencias y pulsamos el nuevo botón de consulta que hemos creado veremos cómo en el log de la aplicación aparecen los valores correctos de cada preferencia. Se mostraría algo como lo siguiente:

10-08 09:27:09.681: INFO/(1162): Opción 1: true
10-08 09:27:09.681: INFO/(1162): Opción 2: prueba
10-08 09:27:09.693: INFO/(1162): Opción 3: FRA

Y hasta aquí hemos llegado con el tema de las preferencias, un tema muy interesante de controlar ya que casi ninguna aplicación se libra de hacer uso de ellas. Existen otras muchas opciones de configuración de las pantallas de preferencias, sobre todo con la llegada de Android 4, pero con lo que hemos visto aquí podremos cubrir la gran mayoría de casos.

Puedes consultar y/o descargar el código completo de los ejemplos desarrollados en este artículo accediendo a la página del curso en GitHub.

Curso de Programación Android en PDF

Este curso también está disponible en PDF. Descubre cómo conseguirlo…

¿Te ha sido de utilidad el Curso de Programación Android? ¿Quieres colaborar de forma económica con el proyecto? Puedes contribuir con cualquier cantidad, unos céntimos, unos euros, cualquier aportación será bienvenida. Además, si tu aportación es superior a una pequeña cantidad simbólica recibirás como agradecimiento un documento con la última versión del curso disponible en formato PDF. Sea como sea, muchas gracias por colaborar!

Más información:

También te puede interesar

16 comentarios

Desarrollo en Android | sgoliver.net blog 13/10/2011 - 10:21

[…] Preferencias en Android II: PreferenceActivity [Nuevo!] […]

Responder
Jose Alvarez 16/10/2011 - 19:51

Hola

He leído que también puedo definir la propiedad android:dialogLayout para especificar un dialogo personalizado. Bien lo he intentado creando un layout que contiene un TextBox y un Button que debe rellenar el TextBox con datos del dispositivo, pero no se como ni donde implementar la funcionalidad.

Podria Ud. si no es mucha molestia, darme un poco de luz en este tema.

Gracias.

Responder
jaio 18/10/2011 - 8:44

Hola,
Primero de todo darte las gracias por este fantastico tutorial que has puesto al alcance de todos, de verdad que es de lo mejor que he visto por internet. El problema con el que me encuentro en este ejemplo es que no se dondo esta el log, por lo que no puedo ver si las preferencias se han guardado correctamente. Puede que sea una pregunta un poco tonta, pero es que aunque tengo experiencia en programacion, soy bastante nueva con eclipse.
Tambien e intentado ver si los datos estan correctamente guardados añadiendole una linea despues de todos los «log.i» para que apareciesen en la pantalla, de esta forma:

lblMensaje.setText(«», «Opción 2: » + pref.getString(«opcion2», «»));

Pero me da un error en esa linea.
Por supuesto, he incluido en el main.xml lo siguiente:

Y en el R.java se ha creado bien:

public static final int LblMensaje=0x7f070002;

gracias

Responder
jaio 18/10/2011 - 8:46

Lo siento, no se me ha copiado bien en el mensaje anterior. Lo que he incluido en el main.xml es lo siguiente:

Responder
Alvaro Jose W 18/01/2013 - 15:40

Hola.
Excelente Articulo…

En este caso solo debo anotar que el archivo «codigospaises.xml» deve estar en la carpeta «res/values» y no en la carpeta «res/xml», como se mensiona en el articulo, me imagino que fue un error de digitacion :)..

Thanks

Responder
angel 03/05/2013 - 20:09

lo tengo todo echo tal cual pero en la class PantallaOpciones en el codigo la parte de..

addPreferencesFromResource(R.xml.opciones);

me aparece con una exclamacion y addPreferencesFromResource esta tachado con una ralla, tengo creado la carpeta xml dentro de res y el archivo opciones.xml con el codigo copiado, todo sin fallos ni errores ecepto ese que he puesto, porque puede ser? gracias.

Responder
elpre 20/05/2013 - 1:33

Buenas, al igual que angel, me ocurre lo mismo. El mensage, por lo que dices, está deprecated, es decir, que ya no se usa o que dentro de poco va a dejar de usarse. Me gustaría saber si tienes algún conocimiento sobre el tema y como se podría solucionar o lo dejamos así directamente.
Si e sposible, me gustaría que me contestases al email.
Saludos y gracias.

Responder
lhbarbier 25/09/2013 - 0:34

Excelente curso, he avanzado mucho con los capítulos. Felicidades y gracias por este material de tan buena calidad.

Una pregunta, al abrirse la pantalla de Opciones, aunque la pantalla conserva los valores entre configuraciones, no muestra los valores actuales de lo seleccionado en los controles 2 y 3, sino solo al entrar a modificarlas, existe alguna forma que nos facilite ya Android de mostrar los valores seleccionados desde la pantalla de opciones sin tener que estar entrando a cada una y ver que valor tienen???

Gracias

Responder
Paco García 26/09/2013 - 8:50

Hola.
Gracias de nuevo por los artículos. Hay algo que no veo como hacer. Si has dicho que no se hace igual en una versión que otra, ¿cómo hacemos esa diferenciación? supongo que habrá que crear las dos versiones y luego en algún lado ir por un sitio o por otro, ¿no?
Un saludo

Responder
claudia 30/10/2013 - 16:37

Hola.
Gracias de nuevo por los artículos. Hay algo que no veo como hacer. Si has dicho que no se hace igual en una versión que otra, ¿cómo hacemos esa diferenciación? supongo que habrá que crear las dos versiones y luego en algún lado ir por un sitio o por otro, ¿no?
Un saludo

Responder
Ernesto 03/12/2013 - 22:52

quisiera saber como añadir un evento a un item de la lista de preferencias. no se si me explico, lo que quiero hacer es simplemente que al presionar una de las opciones de la lista ocurra algo es decir definir un «onItemClicked».

No se si se me entendio lo qe pregunto. por favor si alguien sabe me puede escribir o dejar un comentario aqui

Saludos.

Responder
yair 05/12/2013 - 23:05

hola la verdad quisiera saber como hacer: tengo un EditTextPreference al seleccionarlo se me desplega una información y e deja modificarla. Quisiera saber si podría hacer algo para que no deja modificarla y solo que el usuario la vea

Responder
Jordi 03/06/2014 - 16:19

Creo que no ha quedado muy clara la disposición del código para Fragments, y además no está en git. Saludos.

Responder
Alberto 30/09/2015 - 12:49

addPreferenceFromResource() se encuentra en estado ‘deprecated’ y se recomienda extender de PreferenceFragment y aparentemente se usa igual, aunque hay que seguir usando PreferenceActivity para APIs anteriores.
¿Podrías actualizar esta sección? Gracias.

Responder
Alberto 30/09/2015 - 12:56

Me pasa por no leer bien…..lo siento.

Responder
Francisco 06/06/2016 - 23:54

Hola, primero que nada darle las gracias por compartir sus artículos ya que me han sido de mucha ayuda para ir aprendiendo .

Quiero saber como puedo hacerle para que al pulsar un botón incluido en un widget me envié a mis preferencias para así configurar nuevos datos a mostrar.

Responder

Dejar un comentario

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información. Aceptar Más Información

Política de Privacidad y Cookies