Preferencias mediante PreferenceActivity en Android

Buenos días, en esta entrada vamos a ver como implementar la funcionalidad de las Preferencias en nuestras aplicaciones Android.


Las preferencias en una aplicación puede ser un factor muy importante, gracias a ellas el usuario puede establecer sus preferencias acorde a sus gustos, y hacer de la app algo mas único para ellos.

En este tutorial vamos a ver lo siguiente:

  • Agregar una Activity de Preferencias.
  • Añadir Categorías en nuestra Activity de Preferencias.
  • Añadir campos de Preferencias en las Categorias.
  • Cargar la Activity de preferencias desde nuestra Activity Principal.
  • Leer una preferencia por código.
  • Modificar una preferencia por código.

Dicho esto, vamos a ponernos manos a la obra:

  • Creación del Proyecto

    Para crear nustro nuevo proyecto, nos vamos a dirigir a File -> New -> Android Application Project. En caso de que no dispongamos de esta última opción en la lista que nos sale, podemos ir a “Other” debajo del todo, y en al ventana que nos aparezca buscarlo, o bien introducir “Android” en el campo de texto y así se filtrarán los resultados.

    Una vez hayamos encontrado la opción, nos dispondremos a elegir un nombre para el proyecto, en mi caso lo he decidido llamar EjemploPreferencias, aunque podeis poner el nombre que deseeis. Las demás opciones podemos dejarlas como están.

    Cuando lleguemos a la ventana del asistente en el que se nos pide que introduzcamos un nombre para la Activity principal, le pondremos el nombre, en mi caso la he llamado PreferenciasActivity, aunque podeis ponerle el nombre que querais. Lo mismo haremos con su archivo layout, en mi caso se llama activity_preferencias.xml.

    Una vez terminado el asistente, nuestro proyecto se habrá creado y podemos empezar a crear nuestra aplicación.

  • PreferenciasActivity

    Una vez creado el proyecto, tendremos creada nuestra primera Activity, al igual que su recurso en la carpeta res/layout, el cual definirá su interfaz.

    Vamos a comenzar pués con su Interfaz Gráfica, por tanto vamos a abrir el archivo que encontraremos bajo la carpeta res/layout/activity_preferences.xml el cual modificaremos para dejarlo como vemos a continuación:

    		<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    		    xmlns:tools="http://schemas.android.com/tools"
    		    android:layout_width="match_parent"
    		    android:layout_height="match_parent"
    		    android:orientation="vertical"
    		    tools:context=".PreferenciasActivity" >
    		
    		    <Button
    		        android:id="@+id/btnPreferencias"
    		        android:layout_width="fill_parent"
    		        android:layout_height="wrap_content"
    		        android:onClick="onClickLoad"
    		        android:text="Carga la pantalla de Preferencias" />
    		
    		    <Button
    		        android:id="@+id/btnMostrarValores"
    		        android:layout_width="fill_parent"
    		        android:layout_height="wrap_content"
    		        android:onClick="onClickDisplay"
    		        android:text="Mostrar Valores de las Preferencias" />
    		
    		    <EditText
    		        android:id="@+id/txtString"
    		        android:layout_width="fill_parent"
    		        android:layout_height="wrap_content" />
    		
    		    <Button
    		        android:id="@+id/btnModificarValores"
    		        android:layout_width="fill_parent"
    		        android:layout_height="wrap_content"
    		        android:onClick="onClickModify"
    		        android:text="Modificar valores de Preferencias" />
    		
    		</LinearLayout>
    		

    Como vemos, tenemos un primer Button ()@id/btnPreferencias), el cual será el encargado de abrir la pantalla de preferencias, lo cual vamos a estudiar después.

    El segundo Button (@id/btnMostrarValores) se va a encargar de mostrar una preferencia mediante un mensaje Toast. La diferencia es que accederemos a esta preferencia a través del código y con la ayuda de la clase SharedPreferences, la cual es encargada de las preferencias.

    Tras los 2 primeros Button vemos que tenemos un EditText (@id/txtString), en el cual vamos a introducir mas adelante un valor y con la ayuda del siguiente Button (@id/btnModificarValores) modificaremos la preferencia, de nuevo con la ayuda de la clase SharedPreferences.

    Respecto a nuestra clase PreferenciasActivity.java, la podemos encontrar en la carpeta src/package_name/PreferenciasActivity.java. Esta clase se encargará de dar una funcionalidad a los Button, para mostrar/editar preferencias en la actividad. Su código es el siguiente:

    		package sekth.droid.Preferences;
    
    		import android.app.Activity;
    		import android.content.Intent;
    		import android.content.SharedPreferences;
    		import android.os.Bundle;
    		import android.view.View;
    		import android.widget.EditText;
    		import android.widget.Toast;
    		
    		public class PreferenciasActivity extends Activity {
    		
    			@Override
    			protected void onCreate(Bundle savedInstanceState) {
    				super.onCreate(savedInstanceState);
    				setContentView(R.layout.activity_preferencias);
    		
    			}
    		
    			public void onClickLoad(View v) {
    				Intent i = new Intent("sekth.droid.Preferences.AppPreferenceActivity");
    				startActivity(i);
    			}
    		
    			public void onClickDisplay(View v) {
    				SharedPreferences appPrefs = getSharedPreferences(
    						"sekth.droid.Preferences_preferences", MODE_PRIVATE);
    				DisplayText(appPrefs.getString("EditTextPref", ""));
    			}
    		
    			public void onClickModify(View v) {
    				EditText txtString = (EditText) findViewById(R.id.txtString);
    				SharedPreferences appPrefs = getSharedPreferences(
    						"sekth.droid.Preferences_preferences", MODE_PRIVATE);
    				SharedPreferences.Editor prefsEditor = appPrefs.edit();
    				prefsEditor.putString("EditTextPref", txtString.getText().toString());
    				prefsEditor.commit();
    			}
    		
    			public void DisplayText(String str) {
    				Toast.makeText(getBaseContext(), str, Toast.LENGTH_SHORT).show();
    			}
    		
    		}
    		

    Ahora mismo, tal como vemos seguramente no entendamos nada de lo que vemos, pero vamos a ir enfocandonos método a método para saber exactamente que hace cada Button.

    • public void onClick(View v)

      Vemos su código:

      				public void onClickLoad(View v) {
      					Intent i = new Intent("sekth.droid.Preferences.AppPreferenceActivity");
      					startActivity(i);
      				}
      				

      En este método lo único que hacemos es crear un objeto de la clase Intent, el cual construimos con el argumento “sekth.droid.Preferences/AppPreferencesActvity”. Si analizamos bien, el String que hemos pasado como argumento es el nombre de mi paquete, que para este proyecto ha sido “sekth.droid.Preferences”. Sin embargo, vemos que finaliza con el String “AppPreferencesActivity”. ¿Qué es esto? Esto es una clase, que nos podemos volver locos buscandola, pero que en realidad aún no hemos creado, por tanto vamos a ello.

      • AppPreferencesActivity.java

        Esta clase es una Activity nueva, por tanto la crearemos en nuestro package, y le daremos el nombre AppPreferencesActivity. La curiosidad de esta Activity la veremos en su código:

        						package sekth.droid.Preferences;
        
        						import android.os.Bundle;
        						import android.preference.PreferenceActivity;
        						
        						public class AppPreferenceActivity extends PreferenceActivity {
        						
        							@Override
        							protected void onCreate(Bundle savedInstanceState) {
        								// TODO Auto-generated method stub
        								super.onCreate(savedInstanceState);
        								// Carga las preferencias desde un archivo XML
        								addPreferencesFromResource(R.xml.myapppreferences);
        							}
        							
        						}
        						

        Como vemos, no hereda directamente de la clase Activity, sino de PreferenceActivity. Esto significa que esta Activity será la encargada de mostrar las preferencias de la aplicación, por tanto tiene un comportamiento diferente, es decir, es una Activity especializada para hacer mas fácil el trabajo de las preferencias.

        Como toda Activity, está también puede tener su archivo layout, pero este es diferente al resto, no se crea en la carpeta res/layout, sino que se crea en la carpeta res/xml.

        Para ello vamos a hacer click derecho sobre la carpeta res, e iremos a New -> Folder. A esta carpeta la llamaremos xml.

        Una vez creada, nos disponemos a crear un nuevo archivo dentro de ella (click derecho en la carpeta xml -> New -> Android XML File) y le pondremos de nombre myapppreferences.xml, aunque podeis ponerle otro cualquiera, pero así es mas intuitivo.

        Ahora vamos a añadir lo siguiente en nuestro archivo myapppreferences.xml:

        						<?xml version="1.0" encoding="utf-8"?>
        						<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
        						
        						    <PreferenceCategory android:title="Category1" >
        						        <CheckBoxPreference
        						            android:defaultValue="false"
        						            android:key="checkboxPref"
        						            android:summary="True or False"
        						            android:title="Checkbox" />
        						    </PreferenceCategory>
        						    <PreferenceCategory android:title="Category2" >
        						        <EditTextPreference
        						            android:defaultValue="[Introduce un string aqui]"
        						            android:key="EditTextPref"
        						            android:summary="Introduce un String"
        						            android:title="EditText" />
        						
        						        <RingtonePreference
        						            android:key="ringtonePref"
        						            android:summary="Selecciona un Tono"
        						            android:title="RingTones" />
        						
        						        <PreferenceScreen
        						            android:key="secondPrefScreenPref"
        						            android:summary="Pulse aquí para ir a la segunda pantalla de Preferencias"
        						            android:title="Segunda Pantalla Preferencias" >
        						            <EditTextPreference
        						                android:key="secondEditTextPref"
        						                android:summary="Introduce un String"
        						                android:title="EditText (Segunda Pantalla)" />
        						        </PreferenceScreen>
        						    </PreferenceCategory>
        						
        						</PreferenceScreen>
        						

        Así a ojo podemos apreciar que tenemos unos elementos intuitivos, como puede ser un CheckBox, un EditText, pero que no son como los que conocemos, sino que su especialidad son las preferencias. Vamos a analizar sus elementos:

        • PreferenceScreen: Este atributo define una Pantalla de preferencias. Esto nos servirá para establecer diferentes pantallas de Preferencias, para no tenerlas todas en una misma pantalla y tengamos que desplazarnos hacia abajo continuamente. Es meramente una opción para dividir las preferencias en pantallas, e igualmente a continuación veremos otra forma de “agrupar” las preferencias, mediante el elemento .
        • PreferenceCategory: Esto agrega categorías a nuestras Preferencias, es decir, agrupará las preferencias que tendremos por Categorías. Esto quiere decir que las preferencias que sean del mismo tipo (Opciones de Pantalla, Opciones de texto, Opciones de sonido, etc) las agrupará bajo una misma categoría.
        • CheckBoxPreference: Este es un tipo de View el cual mostrará una opción cuyo valor va a ser boolean, es decir, True o False. En este elemento vemos que le hemos establecido un valor por defecto, “false” (será un checkBox sin marcar), su key o clave será “checkBoxPref”, este atributo nos servirá para acceder a el mediante el código, ya que la clase usada para esto, SharedPreferences, necesita un key para obtener el valor. El atributo “android:summary” es un breve resumen sobre lo que hace esta preferencia. El atributo “android:title” nos define el titulo de esta preferencia.
        • En este punto vamos a cerrar el nodo . Hasta ahora tenemos una categoría, llamada en nuestro caso Category1, y bajo esta categoría se encontrará el CheckBox.

        • PreferenceCategory: Aquí tenemos una segunda categoría, llamada Category2, en la cual introduciremos mas elementos de preferencias.
        • EditTextPreference: Este view, como su nombre indica, es el usado para meter algún tipo de dato de texto o String. Como atributos tenemos “android:defaultValue” el cual establece un valor por defecto en el EditText. El atributo “android:key” el cual tiene la clave de la preferencia, como dijimos antes, para acceder a estos valores a través del código Java, usaremos la clase SharedPreferences que se rige por key-value, y sabiendo la clave podremos obtener el valor. El siguiente atributo es “android:summary”, el cual nos ofrece una descripción de la preferencia. Por último, el atributo “android:title” nos ofrece el título de esta Preferencia. Cabe destacar que una vez ejecutemos nuestra aplicación, este EditText se nos mostrará como si fuera un Dialog, con los botones Ok y Cancel, lo cual veremos mas tarde.
        • RingTonePreference: Esta Preferencia es aquella que se encarga de las Preferencias que tienen que ver con tonos llamada o cosas por el estilo. Sus atributos los hemos visto con anterioridad, “android:key” es la clave de la preferencia, “android:summary” es un pequeño y breve resumen sobre lo que hace esta preferencia y “android:title” es el encargado de dar el nombre a la preferencia. En este caso, cuando pulsemos sobre esta preferencia tendremos otra especie de Dialog, con una lista para elegir entre varios items.
        • PreferenceScreen: Vemos que hemos abierto otro nodo de que dijimos que servía para mostrar pantallas de preferencia. En nuestro caso, al estar dentro de otro, servirá como un botón q ue abrirá otra pantalla de preferencias en la cual se mostrarán los elementos que hay dentro de el.

        • EditTextPreference: Ya lo hemos visto antes, el único cambio que habrá es que este aparecerá en otra pantalla de Preferencias.

        En este punto se cierran el resto de elementos, por tanto ya tenemos creada nuestra pantalla de preferencias, o por así decirlo, nuestra GUI de Preferencias.

        Al ser una Activity, no se nos puede olvidar declararla en el AndroidManifest.xml:

        						<?xml version="1.0" encoding="utf-8"?>
        						<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        						    package="sekth.droid.Preferences"
        						    android:versionCode="1"
        						    android:versionName="1.0" >
        						
        						    <uses-sdk
        						        android:minSdkVersion="8"
        						        android:targetSdkVersion="17" />
        						
        						    <application
        						        android:allowBackup="true"
        						        android:icon="@drawable/ic_launcher"
        						        android:label="@string/app_name"
        						        android:theme="@style/AppTheme" >
        						        <activity
        						            android:name="sekth.droid.Preferences.PreferenciasActivity"
        						            android:label="@string/app_name" >
        						            <intent-filter>
        						                <action android:name="android.intent.action.MAIN" />
        						
        						                <category android:name="android.intent.category.LAUNCHER" />
        						            </intent-filter>
        						        </activity>
        						        <activity
        						            android:name=".AppPreferenceActivity"
        						            android:label="@string/app_name"
        						            android:parentActivityName="sekth.droid.Preferences.PreferenciasActivity" >
        						            <intent-filter>
        						                <action android:name="sekth.droid.Preferences.AppPreferenceActivity" />
        						
        						                <category android:name="android.intent.category.DEFAULT" />
        						            </intent-filter>
        						        </activity>
        						    </application>
        						
        						</manifest>
        						
    • public void onClickDisplay(View v)

      Su código es el siguiente:

      				public void onClickDisplay(View v) {
      					SharedPreferences appPrefs = getSharedPreferences(
      							"sekth.droid.Preferences_preferences", MODE_PRIVATE);
      					DisplayText(appPrefs.getString("EditTextPref", ""));
      				}
      							

      En este método vamos a instanciar la clase SharedPreferences, la cual nos va a servir para abrir nuestro archivo de preferencias manualmente, y en este método, obtener las preferencias de una preferencia en cuestión. Como vemos pasamos como argumentos al método getSharedPreferences() el nombre de nuestro archivo de preferencias, el cual se creó con anterioridad desde nuestra PreferenceActivity, y el segundo argumento es MODE_PRIVATE, el cuál hará que nuestro archivo solo sea abierto por nuestra aplicación o una aplicación con la misma ID.

      Lo siguiente es llamar al método DisplayText, el cual solo se encarga de mostrar un mensaje mediante la clase Toast, y al cuál le hemos pasado como argumento el valor de la preferencia cuya clave es “editTextPref”. Esto es lo que hablamos con anterioridad sobre los key-value, para acceder a un valor determinado, tenemos que saber su key. Como segundo argumento pasamos un valor por defecto, en el caso de que no exista.

      							public void DisplayText(String str) {
      					Toast.makeText(getBaseContext(), str, Toast.LENGTH_SHORT).show();
      				}
      				
    • public void onClickModify(View v)

      Vemos su código:

      				public void onClickModify(View v) {
      					EditText txtString = (EditText) findViewById(R.id.txtString);
      					SharedPreferences appPrefs = getSharedPreferences(
      							"sekth.droid.Preferences_preferences", MODE_PRIVATE);
      					SharedPreferences.Editor prefsEditor = appPrefs.edit();
      					prefsEditor.putString("EditTextPref", txtString.getText().toString());
      					prefsEditor.commit();
      				}
      				

      En este método vamos a modificar una preferencia, en este caso vamos a modificar de nuevo la preferencia cuyo key es “EditTextPref”.

      Para ello vamos a hacer lo mismo que antes, vamos a instanciar la clase SharedPreferences, donde obtendremos las preferencias de nuestra aplicación, y lo siguiente que haremos es crear un nuevo objeto SharedPreferences.Editor para poder editarlas.

      Ahora vamos a poner un texto en nuestra preferencia cuya clave es “editTextPref” mediante la llamada al método putString del objeto prefsEditor, al cual le hemos pasado el key, y el nuevo valor, que es lo que hemos escrito en el EditText.

      Para terminar, es necesario hacer un commit() para guardar estos valores y se logra mediante la llamada al método commit() del objeto prefsEditor, prefsEditor.commit();.

Nuestra aplicación es ahora funcional, y podemos ejecutarla para comprobar sus resultados. Podemos ver unas capturas de pantalla para ver como queda finalmente nuestra aplicación:

Pantalla Principal PreferencesActivity 1 PreferencesActivity 1 EditText PreferencesActivity 1 Ringtone Preferences Activity 2 PreferencesActivity 2 EditText Pantalla Principal Mostrar

A continuación un vídeo para ver la aplicación en ejecución, y modificando algunas preferencias:


Vamos a ver ahora que pasa cuando hemos abierto esta PreferencesActivity. Si ahora en nuestro Eclipse nos vamos a la pestaña de DDMS, y vamos a la pestaña de File Explorer, podemos desplegar el arbol de ficheros que tiene actualmente el Emulador. Si nos dirigimos a Data/Data/nombre-del-paquete, vemos que hay una carpeta llamada shared_prefs:

DDMS SharedPreferences

Dentro de esta carpeta vemos que hay un archivo llamado “sekth.droid.Preferences_preferences.xml. Si abrimos este archivo, obtendremos un xml cuyo contenido es el siguiente en mi caso:

<map>
	<string name="EditTextPref">Usuario</string>
	<string name="secondEditTextPref">Password</string>
	<string name="ringtonePref">content://media/internal/audio/media/30</string>
	<boolean name="checkboxPref" value="false"/>
</map>


Con este tutorial hemos visto 2 aspectos importantes sobre las Preferencias de las Aplicaciones, las cuales por supuesto se basarán en el tipo de aplicación que estemos haciendo.

Sin embargo, desde la API 11 existe otro método para implementar las Preferencias, y es mediante el uso de los Fragment, ya que existe un tipo de Fragment especializado para este trabajo, PreferenceFragment, el cual veremos en otra entrada.

El código de este ejemplo podemos descargarlo de aquí.

Sin más, cualquier corrección o aporte es bienvenido.

Saludos!!!

  • Carmen

    Hola, me estreno escribiendo un comentario por la red.
    Y creo que es necesario dar las gracias a personas como tú, ya que a otras como a mí nos facilitas la comprensión de esta tareas. Te animo a que continues.
    Muchas gracias por compartirlo y por explicarlo tan bien.
    Un saludo

  • yoyito

    Gracias amigo. excelente trabajo.

    Que estes bien.

    • Buenas yoyito,

      Gracias y me alegro de que te pueda haber sido de ayuda.

      Saludos!