Skip to content

Most visited

Recently visited

navigation

Configuración

Las aplicaciones generalmente incluyen una configuración que permite que los usuarios modifiquen las funciones y los comportamientos de las apps. Por ejemplo, algunas apps permiten que los usuarios especifiquen si se habilitan las notificaciones o que especifiquen con qué frecuencia la aplicación sincroniza los datos con la nube.

Si deseas proporcionar una configuración para tu app, debes utilizar las Preference API de Android para crear una interfaz que sea coherente con la experiencia de usuario de otras aplicaciones de Android (incluida la configuración del sistema). En este documento, se describe la manera de crear la configuración de tu app usando las Preference API.

Diseño de configuración

Para obtener información sobre cómo diseñar la configuración, lee la guía de diseño Configuración.

Figura 1: Capturas de pantalla de la configuración de la app Centro de Mensajes de Android. Al seleccionar un elemento definido por una Preference, se abre una interfaz para cambiar la configuración.

Información general

En lugar de utilizar objetos View para crear la interfaz de usuario, la configuración se crea utilizando diversas subclases de la clase Preference que tú declaras en un archivo XML.

Un objeto Preference es el componente esencial de una configuración. Cada Preference aparece como un elemento en una lista y proporciona la interfaz de usuario (IU) correspondiente para que los usuarios modifiquen la configuración. Por ejemplo, una CheckBoxPreference crea un elemento de lista que muestra una casilla de verificación y una ListPreference crea un elemento que abre un cuadro de diálogo con una lista de opciones.

Cada Preference que agregas tiene un par clave-valor correspondiente que el sistema utiliza para guardar la configuración en un archivo SharedPreferences predeterminado para las configuraciones de tu app. Cuando el usuario cambia una configuración, el sistema actualiza el valor correspondiente en el archivo SharedPreferences. El único momento en el que debes interactuar directamente con el archivo SharedPreferences asociado es cuando necesitas leer el valor para determinar el comportamiento de tu app de acuerdo con la configuración del usuario.

El valor guardado en SharedPreferences para cada configuración puede ser uno de los siguientes tipos de datos:

Dado que la IU de configuración de tu app se crea utilizando objetos Preference en lugar de objetos View, debes utilizar una subclase especializada Activity o Fragment para mostrar la configuración de lista:

La forma de configurar tu PreferenceActivity y las instancias de PreferenceFragment se explica en las secciones Creación de una actividad de preferencia y Uso de fragmentos de preferencias.

Preferencias

Cada configuración de tu app está representada por una subclase específica de la clase Preference. Cada subclase incluye un conjunto de propiedades principales que te permiten especificar, por ejemplo, un título para la configuración y el valor predeterminado. Cada subclase también proporciona sus propias propiedades especializadas y su interfaz de usuario. Por ejemplo, en la figura 1 se muestra una captura de pantalla de la configuración de la app Mensajes. Cada elemento de lista de la pantalla de configuración está respaldado por un objeto Preference diferente.

Algunas de las preferencias más comunes son las siguientes:

CheckBoxPreference
Muestra un elemento con una casilla de verificación para una configuración que está habilitada o inhabilitada. El valor guardado es un booleano (true si está marcada).
ListPreference
Abre un cuadro de diálogo con una lista de botones de selección. El valor guardado puede ser cualquiera de los tipos de valores admitidos (enumerados anteriormente).
EditTextPreference
Abre un cuadro de diálogo con un widget EditText. El valor guardado es un String.

Consulta la clase Preference para obtener una lista de todas las otras subclases y sus correspondientes propiedades.

Desde luego, las clases incorporadas no satisfacen todas las necesidades, y es posible que tu aplicación requiera algo más especializado. Por ejemplo, la plataforma actualmente no proporciona una clase Preference para elegir un número o una fecha. Por lo tanto, es posible que debas definir tu propia subclase Preference. Para obtener ayuda para hacerlo, consulta la sección Creación de una preferencia personalizada.

Definición de preferencias en XML

Si bien puedes crear instancias de objetos Preference nuevos en tiempo de ejecución, debes definir tu lista de configuración en XML con una jerarquía de objetos Preference . Se prefiere utilizar un archivo XML para definir tu colección de configuraciones porque el archivo proporciona una estructura fácil de leer que se puede actualizar simplemente. Además, las configuraciones de tu app generalmente están predeterminadas, aunque tú puedes modificar la colección en tiempo de ejecución.

Cada subclase Preference puede declararse con un elemento XML que coincida con el nombre de la clase, como <CheckBoxPreference>.

Debes guardar el archivo XML en el directorio res/xml/. Si bien puedes asignar el nombre que desees al archivo, tradicionalmente se llama preferences.xml. Por lo general, necesitas un solo archivo, porque las ramas de la jerarquía (que abren su propia lista de configuraciones) se declaran utilizando instancias anidadas de PreferenceScreen.

Nota: Si deseas crear un diseño multipanel para tu configuración, necesitas archivos XML separados para cada fragmento.

El nodo raíz del archivo XML debe ser un elemento <PreferenceScreen>. Dentro de este elemento, agregas cada Preference. Cada elemento secundario que agregas dentro del elemento <PreferenceScreen> aparece como un elemento en la lista de configuración.

Por ejemplo:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <CheckBoxPreference
        android:key="pref_sync"
        android:title="@string/pref_sync"
        android:summary="@string/pref_sync_summ"
        android:defaultValue="true" />
    <ListPreference
        android:dependency="pref_sync"
        android:key="pref_syncConnectionType"
        android:title="@string/pref_syncConnectionType"
        android:dialogTitle="@string/pref_syncConnectionType"
        android:entries="@array/pref_syncConnectionTypes_entries"
        android:entryValues="@array/pref_syncConnectionTypes_values"
        android:defaultValue="@string/pref_syncConnectionTypes_default" />
</PreferenceScreen>

En este ejemplo, hay una CheckBoxPreference y una ListPreference. Ambos elementos incluyen los siguientes tres atributos:

android:key
Este atributo se requiere para las preferencias que continúan con un valor de datos. Especifica la clave única (una string) que el sistema usa cuando guarda el valor de esta configuración en las SharedPreferences.

Este atributo no es necesario únicamente cuando la preferencia es una PreferenceCategory o PreferenceScreen, o la preferencia especifica un Intent para invocar (con un elemento <intent>) o un Fragment para mostrar (con un atributo android:fragment).

android:title
Esto proporciona un nombre de la configuración visible para el usuario.
android:defaultValue
Esto especifica el valor inicial que el sistema debe configurar en el archivo SharedPreferences. Debes proporcionar una valor predeterminado para todas las configuraciones.

Para obtener información sobre todos los demás atributos admitidos, consulta la documentación de Preference (y la subclase respectiva).

Figura 2: Configuración de categorías con títulos.
1. La categoría se especifica con el elemento <PreferenceCategory>.
2. El título se especifica con el atributo android:title.

Cuando la lista de configuración supera los 10 elementos, puedes agregar títulos para definir grupos de configuraciones o mostrar estos grupos en una pantalla separada. Estas opciones se describen en las secciones siguientes.

Creación de grupos de configuración

Si presentas una lista de 10 o más configuraciones, es posible que a los usuarios les resulte difícil explorarlos, comprenderlos y procesarlos. Para solucionar esto, puedes dividir algunas o todas las configuraciones en grupos, y convertir una lista larga en varias listas más cortas. Un grupo de configuraciones relacionadas puede presentarse de una de dos formas:

Puedes utilizar una de estas técnicas de agrupación, o ambas, para organizar la configuración de tu app. Para decidir qué técnica utilizar y cómo dividir las configuraciones, debes seguir las pautas de la guía de Configuración de Diseño Android.

Utilización de títulos

Si deseas proporcionar divisores con encabezados entre grupos de configuraciones (como se muestra en la figura 2), coloca cada grupo de objetos Preference dentro de una PreferenceCategory.

Por ejemplo:

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory
        android:title="@string/pref_sms_storage_title"
        android:key="pref_key_storage_settings">
        <CheckBoxPreference
            android:key="pref_key_auto_delete"
            android:summary="@string/pref_summary_auto_delete"
            android:title="@string/pref_title_auto_delete"
            android:defaultValue="false"... />
        <Preference
            android:key="pref_key_sms_delete_limit"
            android:dependency="pref_key_auto_delete"
            android:summary="@string/pref_summary_delete_limit"
            android:title="@string/pref_title_sms_delete"... />
        <Preference
            android:key="pref_key_mms_delete_limit"
            android:dependency="pref_key_auto_delete"
            android:summary="@string/pref_summary_delete_limit"
            android:title="@string/pref_title_mms_delete" ... />
    </PreferenceCategory>
    ...
</PreferenceScreen>

Utilización de subpantallas

Si deseas colocar grupos de configuraciones en una subpantalla (como se muestra en la figura 3), dispón el grupo de objetos Preference dentro de una PreferenceScreen.

Figura 3: Configuración de subpantallas. El elemento <PreferenceScreen> crea un elemento que, cuando se selecciona, abre una lista separada para mostrar las configuraciones anidadas.

Por ejemplo:

<PreferenceScreen  xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- opens a subscreen of settings -->
    <PreferenceScreen
        android:key="button_voicemail_category_key"
        android:title="@string/voicemail"
        android:persistent="false">
        <ListPreference
            android:key="button_voicemail_provider_key"
            android:title="@string/voicemail_provider" ... />
        <!-- opens another nested subscreen -->
        <PreferenceScreen
            android:key="button_voicemail_setting_key"
            android:title="@string/voicemail_settings"
            android:persistent="false">
            ...
        </PreferenceScreen>
        <RingtonePreference
            android:key="button_voicemail_ringtone_key"
            android:title="@string/voicemail_ringtone_title"
            android:ringtoneType="notification" ... />
        ...
    </PreferenceScreen>
    ...
</PreferenceScreen>

Utilización de intents

En algunos casos, quizá desees que un elemento de preferencia abra una actividad diferente, en lugar de una pantalla de configuración, por ejemplo, un navegador web para ver una página web. Para invocar un Intent cuando el usuario seleccione un elemento de preferencia, agrega un elemento <intent> como campo secundario del elemento <Preference> correspondiente.

Por ejemplo, a continuación se explica cómo puedes utilizar un elemento de preferencia para abrir una página web:

<Preference android:title="@string/prefs_web_page" >
    <intent android:action="android.intent.action.VIEW"
            android:data="http://www.example.com" />
</Preference>

Puedes crear intents implícitas y explícitas utilizando los siguientes atributos:

android:action
La acción que se ha de asignar, según el método setAction() .
android:data
Los datos que se han de asignar, según el método setData().
android:mimeType
El tipo de MIME que se ha de asignar, según el método setType() .
android:targetClass
La parte de la clase del nombre del componente, según el método setComponent().
android:targetPackage
Parte del paquete del nombre del componente, según el método setComponent().

Creación de una actividad de preferencia

Para mostrar tu configuración en una actividad, extiende la clase PreferenceActivity. Es una extensión de la clase Activity tradicional que muestra una lista de configuración basada en una jerarquía de objetos Preference. PreferenceActivity mantiene automáticamente la configuración asociada con cada Preference cuando el usuario realiza un cambio.

Nota: Si desarrollas una aplicación para Android 3.0 y versiones superiores, debes utilizar PreferenceFragment. Consulta la sección siguiente, Utilización de fragmentos de preferencia.

Lo más importante que debes recordar es que no se carga un diseño de vistas durante el callback onCreate(). En lugar de ello, se llama a addPreferencesFromResource() para agregar las preferencias que declaraste en un archivo XML para la actividad. Por ejemplo, este es el código mínimo vacío que se requiere para una PreferenceActivity funcional:

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

En realidad, este código es suficiente para algunas apps, porque apenas el usuario modifica una preferencia, el sistema guarda los cambios en un archivo SharedPreferences predeterminado que los otros componentes de tu aplicación pueden leer cuando necesitas comprobar la configuración del usuario. Sin embargo, muchas apps requieren un poco más de código para escuchar los cambios que se realizan a las preferencias. Para obtener información sobre la forma de escuchar los cambios en el archivo SharedPreferences, consulta la sección Lectura de preferencias.

Utilización de fragmentos de preferencia

Si desarrollas una app para Android 3.0 (nivel de API 11) y versiones posteriores, debes usar un PreferenceFragment para mostrar tu lista de objetos Preference. Puedes agregar un PreferenceFragment a cualquier actividad; no necesitas utilizar una PreferenceActivity.

Los fragmentos proporcionan una arquitectura más flexible para tu aplicación, en comparación con el uso exclusivo de actividades, independientemente de la clase de actividad que esté desarrollando. Por lo tanto, usar PreferenceFragment para controlar la pantalla de tu configuración en lugar de PreferenceActivity siempre que sea posible.

Tu implementación de PreferenceFragment puede ser tan simple como la definición del método onCreate() para cargar un archivo de preferencias con addPreferencesFromResource(). Por ejemplo:

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

        // Load the preferences from an XML resource
        addPreferencesFromResource(R.xml.preferences);
    }
    ...
}

Luego puedes agregar este fragmento a una Activity de la misma forma que lo haría para cualquier otro Fragment. Por ejemplo:

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

        // Display the fragment as the main content.
        getFragmentManager().beginTransaction()
                .replace(android.R.id.content, new SettingsFragment())
                .commit();
    }
}

Nota: Un PreferenceFragment no tiene su propio objeto Context. Si necesitas un objeto Context , puedes llamar a getActivity(). Sin embargo, solo debes llamar a getActivity() cuando el fragmento está adjuntado a una actividad. Cuando el fragmento aún no se haya anexado, o se haya separado durante el final de su ciclo de vida, getActivity() mostrará null.

Configuración de valores predeterminados

Las preferencias que creas probablemente definirán algunos comportamientos importantes para tu aplicación; por ende, es necesario que inicialices el archivo SharedPreferences asociado con valores predeterminados para cada Preference cuando el usuario abre por primera vez tu aplicación.

Lo primero que debes hacer es especificar un valor predeterminado para cada objeto Preference en tu archivo XML mediante el atributo android:defaultValue. El valor puede ser cualquier tipo de de dato que sea adecuado para el objeto Preference correspondiente. Por ejemplo:

<!-- default value is a boolean -->
<CheckBoxPreference
    android:defaultValue="true"
    ... />

<!-- default value is a string -->
<ListPreference
    android:defaultValue="@string/pref_syncConnectionTypes_default"
    ... />

Luego, desde el método onCreate() en la actividad principal de tu aplicación ,y en cualquier otra actividad a través de la cual el usuario pueda ingresar en tu aplicación por primera vez, llama a setDefaultValues():

PreferenceManager.setDefaultValues(this, R.xml.advanced_preferences, false);

Esta llamada durante onCreate() garantiza que tu aplicación se inicialice correctamente con la configuración predeterminada, que tu aplicación quizá necesite leer para determinar algunos comportamientos (por ejemplo, si se descargan datos mientras se utiliza una red móvil).

Este método adopta tres argumentos:

Siempre que configures el tercer argumento como false, puedes llamar de forma segura a este método cada vez que se inicia tu actividad sin reemplazar las preferencias guardadas del usuario restableciéndolas con los valores predeterminados. Sin embargo, si lo configuras como true, reemplazarás los valores anteriores con los valores predeterminados.

Utilización de encabezados de preferencia

En casos aislados, quizá desees diseñar tu configuración de forma tal que la primera pantalla muestre solo una lista de subpantallas (por ejemplo, en la app Configuración del sistema, como se muestra en las figuras 4 y 5). Cuando desarrollas este diseño para Android 3.0 y versiones posteriores, debes usar la función de “encabezados” en lugar de crear subpantallas con elementos PreferenceScreen anidados.

Para crear tu configuración con encabezados, debes hacer lo siguiente:

  1. Separa cada grupo de configuraciones en instancias independientes de PreferenceFragment. Es decir, cada grupo de configuraciones necesita un archivo XML independiente.
  2. Crea un archivo de encabezados XML que enumere cada grupo de configuraciones y declare qué fragmento contiene la lista de configuraciones correspondiente.
  3. Extiende la clase PreferenceActivity para alojar tu configuración.
  4. Implementa el callback onBuildHeaders() para especificar el archivo de encabezados.

Un gran beneficio de utilizar este diseño es que PreferenceActivity presenta automáticamente el diseño de dos paneles que se muestra en la figura 4 cuando se ejecuta en pantallas grandes.

Incluso si tu aplicación admite versiones de Android anteriores a 3.0, puedes crear tu aplicación para utilizar PreferenceFragment para una presentación de dos paneles en dispositivos más nuevos y a la vez, admitir una jerarquía tradicional multipantalla en dispositivos más antiguos (consulta la sección Admisión de versiones anteriores con encabezados de preferencia).

Figura 4: Diseño de dos paneles con encabezados.
1. Los encabezados se definen con un archivo de encabezados XML.
2. Cada grupo de configuraciones se define con un PreferenceFragment especificado por un elemento <header> en el archivo de encabezados.

Figura 5: Teléfono celular con encabezados de configuración. Cuando se selecciona un elemento, el PreferenceFragment asociado reemplaza los encabezados.

Creación del archivo de encabezados

Un solo elemento <header> dentro de un elemento raíz <preference-headers> especifica cada grupo de configuraciones de tu lista de encabezados. Por ejemplo:

<?xml version="1.0" encoding="utf-8"?>
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
    <header
        android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentOne"
        android:title="@string/prefs_category_one"
        android:summary="@string/prefs_summ_category_one" />
    <header
        android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentTwo"
        android:title="@string/prefs_category_two"
        android:summary="@string/prefs_summ_category_two" >
        <!-- key/value pairs can be included as arguments for the fragment. -->
        <extra android:name="someKey" android:value="someHeaderValue" />
    </header>
</preference-headers>

Con el atributo android:fragment, cada encabezado declara una instancia de PreferenceFragment que debe abrirse cuando el usuario selecciona el encabezado.

El elemento <extras> te permite pasar pares clave-valor al fragmento en un Bundle. El fragmento puede obtener los argumentos llamando a getArguments(). Puedes pasar argumentos al fragmento por diversas razones, pero una buena razón es reutilizar la misma subclase de PreferenceFragment para cada grupo y emplear el argumento para especificar el archivo XML de preferencias que el fragmento debe cargar.

Por ejemplo, a continuación se incluye un fragmento que se puede reutilizar para varios grupos de configuraciones cuando cada encabezado define un argumento <extra> con la clave "settings":

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

        String settings = getArguments().getString("settings");
        if ("notifications".equals(settings)) {
            addPreferencesFromResource(R.xml.settings_wifi);
        } else if ("sync".equals(settings)) {
            addPreferencesFromResource(R.xml.settings_sync);
        }
    }
}

Muestra de encabezados

Para mostrar los encabezados de preferencias, debes implementar el método de callback onBuildHeaders() y llamar a loadHeadersFromResource(). Por ejemplo:

public class SettingsActivity extends PreferenceActivity {
    @Override
    public void onBuildHeaders(List<Header> target) {
        loadHeadersFromResource(R.xml.preference_headers, target);
    }
}

Cuando el usuario selecciona un elemento de la lista de encabezados, el sistema abre el PreferenceFragment asociado.

Nota: Cuando se usan encabezados de preferencias, tu subclase de PreferenceActivity no necesita implementar el método onCreate() porque la única tarea que se requiere para la actividad es la carga de los encabezados.

Admisión de versiones anteriores con encabezados de preferencia

Si tu aplicación admite versiones de Android anteriores a 3.0, también puedes utilizar encabezados para proporcionar un diseño de dos paneles cuando se ejecuta en Android 3.0 y versiones posteriores. Todo lo que debes hacer es crear un archivo XML de preferencias adicional que utilice elementos <Preference> básicos que se comporten como los elementos de encabezado (para que sean utilizados por las versiones anteriores de Android ).

Sin embargo, en lugar de abrir una PreferenceScreen nueva, cada uno de los elementos <Preference> envía un Intent a la PreferenceActivity que especifica el archivo XML de preferencias que debe cargarse.

Por ejemplo, a continuación se incluye un archivo XML para los encabezados de preferencias que se usa en Android 3.0 y versiones posteriores (res/xml/preference_headers.xml):

<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
    <header
        android:fragment="com.example.prefs.SettingsFragmentOne"
        android:title="@string/prefs_category_one"
        android:summary="@string/prefs_summ_category_one" />
    <header
        android:fragment="com.example.prefs.SettingsFragmentTwo"
        android:title="@string/prefs_category_two"
        android:summary="@string/prefs_summ_category_two" />
</preference-headers>

El siguiente es un archivo de preferencias que proporciona los mismos encabezados para las versiones anteriores a Android 3.0 (res/xml/preference_headers_legacy.xml):

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <Preference
        android:title="@string/prefs_category_one"
        android:summary="@string/prefs_summ_category_one"  >
        <intent
            android:targetPackage="com.example.prefs"
            android:targetClass="com.example.prefs.SettingsActivity"
            android:action="com.example.prefs.PREFS_ONE" />
    </Preference>
    <Preference
        android:title="@string/prefs_category_two"
        android:summary="@string/prefs_summ_category_two" >
        <intent
            android:targetPackage="com.example.prefs"
            android:targetClass="com.example.prefs.SettingsActivity"
            android:action="com.example.prefs.PREFS_TWO" />
    </Preference>
</PreferenceScreen>

Dado que la compatibilidad con los <preference-headers> se agregó en Android 3.0, el sistema llama a onBuildHeaders() en tu PreferenceActivity solo cuando se ejecuta en Android 3.0 o versiones posteriores. Para cargar el archivo de encabezados “heredado” (preference_headers_legacy.xml), debes comprobar la versión de Android y si esta es anterior a la 3.0 (HONEYCOMB), llama a addPreferencesFromResource() para cargar el archivo de encabezados heredado. Por ejemplo:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
        // Load the legacy preferences headers
        addPreferencesFromResource(R.xml.preference_headers_legacy);
    }
}

// Called only on Honeycomb and later
@Override
public void onBuildHeaders(List<Header> target) {
   loadHeadersFromResource(R.xml.preference_headers, target);
}

Lo único que queda por hacer es manejar la Intent que se pasó a la actividad para identificar qué archivo de preferencia se debe cargar. Recupera la acción de la intent y compárala con las strings de acción conocidas que hayas usado en las etiquetas <intent> del archivo XML de preferencias:

final static String ACTION_PREFS_ONE = "com.example.prefs.PREFS_ONE";
...

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    String action = getIntent().getAction();
    if (action != null && action.equals(ACTION_PREFS_ONE)) {
        addPreferencesFromResource(R.xml.preferences);
    }
    ...

    else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
        // Load the legacy preferences headers
        addPreferencesFromResource(R.xml.preference_headers_legacy);
    }
}

Ten en cuenta que las llamadas consecutivas a addPreferencesFromResource() apilarán todas las preferencias en una sola lista; por lo tanto, asegúrate de que solo se llame una vez encadenando las condiciones con declaraciones else-if.

Lectura de preferencias

De forma predeterminada, todas las preferencias de tu app se guardan en un archivo al que se puede acceder desde cualquier lugar dentro de tu aplicación mediante un llamado al método estático PreferenceManager.getDefaultSharedPreferences(). Esto devuelve el objeto SharedPreferences que contiene todos los pares clave-valor asociados con los objetos Preference usados en tu PreferenceActivity.

Por ejemplo, a continuación se muestra cómo puedes leer uno de los valores de preferencia desde cualquier otra actividad en tu aplicación:

SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
String syncConnPref = sharedPref.getString(SettingsActivity.KEY_PREF_SYNC_CONN, "");

Escucha de los cambios de preferencias

Existen varios motivos por los que quizá desees recibir una notificación apenas el usuario cambia una de las preferencias. Para recibir un callback cuando se realiza un cambio a cualquiera de las preferencias, implementa la interfaz de SharedPreference.OnSharedPreferenceChangeListener y registra el receptor del objeto SharedPreferences llamando a registerOnSharedPreferenceChangeListener().

La interfaz tiene un solo método de callback, onSharedPreferenceChanged() y quizá te resulte más fácil implementar la interfaz como parte de tu actividad. Por ejemplo:

public class SettingsActivity extends PreferenceActivity
                              implements OnSharedPreferenceChangeListener {
    public static final String KEY_PREF_SYNC_CONN = "pref_syncConnectionType";
    ...

    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
        String key) {
        if (key.equals(KEY_PREF_SYNC_CONN)) {
            Preference connectionPref = findPreference(key);
            // Set summary to be the user-description for the selected value
            connectionPref.setSummary(sharedPreferences.getString(key, ""));
        }
    }
}

En este ejemplo, el método comprueba si la configuración cambiada es de una clave de preferencia conocida. Llama a findPreference() para obtener el objeto Preference que se cambió y poder modificar el resumen del elemento para que sea una descripción de la selección del usuario. Es decir, si la configuración es una ListPreference u otra configuración de varias opciones, debes llamar a setSummary() cuando se cambia la configuración para mostrar el estado actual (como la configuración de suspensión que se muestra en la figura 5).

Nota: Como se describe en el documento Configuración de Diseño Android, te recomendamos que actualices el resumen de una ListPreference cada vez que el usuario cambia la preferencia, a fin de describir la configuración actual.

Para lograr una administración correcta del ciclo de vida en la actividad, te recomendamos registrar y anular el registro de tu SharedPreferences.OnSharedPreferenceChangeListener durante los callbacks onResume() y onPause(), respectivamente:

@Override
protected void onResume() {
    super.onResume();
    getPreferenceScreen().getSharedPreferences()
            .registerOnSharedPreferenceChangeListener(this);
}

@Override
protected void onPause() {
    super.onPause();
    getPreferenceScreen().getSharedPreferences()
            .unregisterOnSharedPreferenceChangeListener(this);
}

Advertencia: Cuando llamas a registerOnSharedPreferenceChangeListener(), el administrador de preferencias actualmente no almacena una referencia sólida al receptor. Debes almacenar una referencia fuerte a la escucha (listener); de lo contrario, este será susceptible a la recolección de elementos no utilizados. Te recomendamos que mantengas una referencia a la escucha (listener) en los datos de instancia de un objeto que existirá mientras necesites a la escucha.

Por ejemplo, en el código siguiente, el emisor no mantiene una referencia con la escucha (listener). Como resultado, la escucha (listener) estará sujeta a la recolección de elementos no utilizados, y fallará en algún momento indeterminado del futuro:

prefs.registerOnSharedPreferenceChangeListener(
  // Bad! The listener is subject to garbage collection!
  new SharedPreferences.OnSharedPreferenceChangeListener() {
  public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
    // listener implementation
  }
});

En cambio, almacena una referencia a la escucha (listener) en un campo de datos de instancia de un objeto que existirá mientras se necesite a la misma:

SharedPreferences.OnSharedPreferenceChangeListener listener =
    new SharedPreferences.OnSharedPreferenceChangeListener() {
  public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
    // listener implementation
  }
};
prefs.registerOnSharedPreferenceChangeListener(listener);

Administración del uso de red

A partir de Android 4.0, la aplicación Configuración del sistema permite que los usuarios vean cuántos datos de red utilizan sus aplicaciones en primer plano y en segundo plano. Los usuarios pueden inhabilitar el uso de los datos en segundo plano para apps individuales. Para evitar que los usuarios deshabiliten el acceso de tu app a los datos en segundo plano, debes utilizar la conexión de datos de forma eficiente y permitir que los usuarios definan mejor el uso de datos de tu app a través de la configuración de tu aplicación.

Por ejemplo, puedes permitir que el usuario controle la frecuencia con la que tu app sincroniza los datos, si tu aplicación realiza cargas y descargas solo cuando el dispositivo está conectado con Wi-Fi, si tu app utiliza datos con el servicio de itinerancia, etc. Con estos controles a su disposición, es mucho menos probable que los usuarios inhabiliten el acceso de tu app a los datos cuando se acercan a los límites que establecieron en la configuración del sistema, ya que pueden controlar precisamente la cantidad de datos que tu app usa.

Una vez que hayas agregado las preferencias necesarias en tu PreferenceActivity para controlar los hábitos de datos de tu app, debes agregar un filtro de intents para ACTION_MANAGE_NETWORK_USAGE en tu archivo de manifiesto. Por ejemplo:

<activity android:name="SettingsActivity" ... >
    <intent-filter>
       <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
       <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

Este filtro de intents le indica al sistema que esta es la actividad que controla el uso de datos de tu aplicación. Por lo tanto, cuando el usuario inspecciona cuántos datos usa tu app desde la app de configuración del sistema, se muestra un botón Ver configuración de la aplicación que lanza tu PreferenceActivity para que el usuario pueda definir mejor la cantidad de datos que tu app usa.

Creación de una preferencia personalizada

El framework de Android incluye una variedad de subclases Preference que te permiten crear una IU para varios tipos de configuraciones. Sin embargo, quizá descubras una configuración que necesitas para la cual no haya una solución incorporada, como un selector de números o fechas. Si es así, deberás extender la clase Preference o una de las demás subclases para crear una preferencia personalizada.

Cuando extiendes la clase Preference, hay algunas cosas importantes que debes hacer:

En las siguientes secciones, se describe cómo realizar cada una de estas tareas.

Especificación de la interfaz de usuario

Si extiendes directamente la clase Preference, debes implementar onClick() para definir la acción que ocurre cuando el usuario selecciona el elemento. Sin embargo, la mayoría de las configuraciones personalizadas extienden DialogPreference para mostrar un cuadro de diálogo, lo que simplifica el procedimiento. Cuando extiendes DialogPreference, debes llamar a setDialogLayoutResourcs() en el constructor de la clase para especificar el diseño del cuadro de diálogo.

Por ejemplo, a continuación se muestra el constructor de una DialogPreference personalizada que declara el diseño, y especifica el texto de los botones de diálogo positivos y negativos predeterminados:

public class NumberPickerPreference extends DialogPreference {
    public NumberPickerPreference(Context context, AttributeSet attrs) {
        super(context, attrs);

        setDialogLayoutResource(R.layout.numberpicker_dialog);
        setPositiveButtonText(android.R.string.ok);
        setNegativeButtonText(android.R.string.cancel);

        setDialogIcon(null);
    }
    ...
}

Cómo guardar el valor de configuración

Puedes guardar un valor para la configuración en cualquier momento llamando a uno de los métodos persist*() de la clase Preference, como persistInt() si el valor de la configuración es un valor entero o persistBoolean() para guardar un valor booleano.

Nota: Cada Preference puede guardar solo un tipo de dato, por lo que debes usar el método persist*() adecuado para el tipo de dato que usa tu Preference personalizada.

Cuando eliges continuar, la configuración puede depender de la clase Preference que extiendas. Si extiendes DialogPreference, debes continuar con el valor solo cuando el cuadro de diálogo se cierra debido a un resultado positivo (el usuario selecciona el botón “Aceptar”).

Cuando una DialogPreference se cierra, el sistema llama al método onDialogClosed(). El método incluye un argumento booleano que especifica si el resultado del usuario es "positivo"; si el valor es true, significa que el usuario seleccionó el botón positivo y tú debes guardar el nuevo valor. Por ejemplo:

@Override
protected void onDialogClosed(boolean positiveResult) {
    // When the user selects "OK", persist the new value
    if (positiveResult) {
        persistInt(mNewValue);
    }
}

En este ejemplo, mNewValue es un miembro de clase que contiene el valor actual de la configuración. Al llamar a persistInt() se guarda el valor en el archivo SharedPreferences (utilizando automáticamente la clave especificada en el archivo XML para esta Preference).

Inicialización del valor actual

Cuando el sistema agrega tuPreference a la pantalla, llama a onSetInitialValue() para notificarte si la configuración tiene un valor persistente. Si no hay un valor persistente, esta llamada te proporciona el valor predeterminado.

El método onSetInitialValue() pasa un valor booleano, restorePersistedValue, para indicar si ya hay un valor persistente para la configuración. Si es true, debes recuperar el valor persistente mediante un llamado a uno de los métodos getPersisted*() de la clase Preference, como getPersistedInt() para un valor entero. Por lo general, es recomendable que recuperes el valor persistente, de modo que puedas actualizar de forma adecuada la IU para reflejar el valor guardado anteriormente.

Si restorePersistedValue es false, debes utilizar el valor predeterminado que se pasa en el segundo argumento.

@Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
    if (restorePersistedValue) {
        // Restore existing state
        mCurrentValue = this.getPersistedInt(DEFAULT_VALUE);
    } else {
        // Set default state from the XML attribute
        mCurrentValue = (Integer) defaultValue;
        persistInt(mCurrentValue);
    }
}

Cada método getPersisted*() toma un argumento que especifica el valor predeterminado que se usará en caso de que realmente no haya un valor persistente o la clave no exista. En el ejemplo anterior, se usa una constante local para especificar el valor predeterminado en caso de que getPersistedInt() no pueda mostrar un valor persistente.

Advertencia: No puedes usar defaultValue como valor predeterminado en el método getPersisted*() porque su valor siempre es null cuando restorePersistedValue es true.

Provisión de un valor predeterminado

Si la instancia de tu clase Preference especifica un valor predeterminado (con el atributo android:defaultValue), el sistema llama a onGetDefaultValue() cuando crea una instancia del objeto para obtener el valor. Debes implementar este método para que el sistema guarde el valor predeterminado en SharedPreferences. Por ejemplo:

@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
    return a.getInteger(index, DEFAULT_VALUE);
}

Los argumentos del método proporcionan todo lo que necesitas: la matriz de atributos y la posición de índice de android:defaultValue, que debes recuperar. El motivo por el que debes implementar este método para extraer el valor predeterminado del atributo es que debes especificar un valor predeterminado local para el atributo en caso de que el valor no esté definido.

Cómo guardar y restaurar el estado de Preferencias

Al igual que una View en un diseño, tu subclase Preference es responsable de guardar y restaurar su estado en caso de que la actividad o el fragmento se reinicien (por ejemplo, cuando el usuario gira la pantalla). Para guardar y restaurar correctamente el estado de tu clase Preference, debes implementar los métodos de callback del ciclo de vida onSaveInstanceState() y onRestoreInstanceState().

El estado de tu Preference es definido por un objeto que implementa la interfaz Parcelable. El framework de Android te proporciona este objeto como punto de partida para definir tu objeto de estado: la clase Preference.BaseSavedState.

Para definir cómo tu clase Preference guarda tu estado, debes extender la clase Preference.BaseSavedState. Debes reemplazar solo algunos métodos y definir el objeto CREATOR .

En la mayoría de las apps, puedes copiar la siguiente implementación y simplemente cambiar las líneas que manejan el value si tu subclase Preference guarda un tipo de datos que no sea un valor entero.

private static class SavedState extends BaseSavedState {
    // Member that holds the setting's value
    // Change this data type to match the type saved by your Preference
    int value;

    public SavedState(Parcelable superState) {
        super(superState);
    }

    public SavedState(Parcel source) {
        super(source);
        // Get the current preference's value
        value = source.readInt();  // Change this to read the appropriate data type
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        super.writeToParcel(dest, flags);
        // Write the preference's value
        dest.writeInt(value);  // Change this to write the appropriate data type
    }

    // Standard creator object using an instance of this class
    public static final Parcelable.Creator<SavedState> CREATOR =
            new Parcelable.Creator<SavedState>() {

        public SavedState createFromParcel(Parcel in) {
            return new SavedState(in);
        }

        public SavedState[] newArray(int size) {
            return new SavedState[size];
        }
    };
}

Con la implementación anterior de Preference.BaseSavedState agregada a tu app (generalmente como una subclase de tu subclase Preference), debes implementar los métodos onSaveInstanceState() y onRestoreInstanceState() para tu subclase Preference.

Por ejemplo:

@Override
protected Parcelable onSaveInstanceState() {
    final Parcelable superState = super.onSaveInstanceState();
    // Check whether this Preference is persistent (continually saved)
    if (isPersistent()) {
        // No need to save instance state since it's persistent,
        // use superclass state
        return superState;
    }

    // Create instance of custom BaseSavedState
    final SavedState myState = new SavedState(superState);
    // Set the state's value with the class member that holds current
    // setting value
    myState.value = mNewValue;
    return myState;
}

@Override
protected void onRestoreInstanceState(Parcelable state) {
    // Check whether we saved the state in onSaveInstanceState
    if (state == null || !state.getClass().equals(SavedState.class)) {
        // Didn't save the state, so call superclass
        super.onRestoreInstanceState(state);
        return;
    }

    // Cast state to custom BaseSavedState and pass to superclass
    SavedState myState = (SavedState) state;
    super.onRestoreInstanceState(myState.getSuperState());

    // Set this Preference's widget to reflect the restored state
    mNumberPicker.setValue(myState.value);
}
This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Follow Google Developers on WeChat

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a short survey?
Help us improve the Android developer experience. (Dec 2017 Android Platform & Tools Survey)