Tipos De Preferencias En Android

Construir una pantalla de ajustes/configuración es soportado por una variedad de tipos de preferencias en Android. Verás que tienes a disposición varios patrones que usarán los controles de UI de Android para permitirte especificar el valor de la preferencia.

Apariencia de los tipos de preferencias en AndroidX

Con el fin de cubrir esta gama de herramientas, en este tutorial aprenderás sobre:

  • Preferencias con entrada de texto
  • Preferencias con lista de selección única
  • Preferencias con lista de selección múltiple
  • Preferencias con control deslizador
  • Preferencias con switch

Nota: Este es el segundo tutorial de la guía de ajustes de usuario en Android, por lo que asumiré que ya leíste Crear Fragmento De Preferencias.


Ejemplo De Tipos De Preferencias En Android

Para materializar el uso de los tipos de preferencias que existen, extenderemos la jerarquía de preferencias del proyecto actual de esta forma:

App ejemplo para tipos de preferencias en AndroidX

Como ves, tendremos. Puedes descargar el proyecto en Android Studio desde el siguiente enlace. El módulo correspondiente a este tutorial será P2_Tipos_De_Preferencias:


Tipos De Preferencias

Iniciemos por conocer las diferentes subclases de la clase Preference que aportan nuevas funcionalidades desde la interfaz y selección del valor de configuración:

Herencias de la clase Preference en Android

Cada uno de estos bloques está asociado a un patrón que brinda una forma diferente para configurar los comportamientos del aplicativo. Los siguientes son los propósitos de cada componente.


EditTextPreference

En nuestro ejemplo tenemos una situación hipotética donde necesitamos recibir el nombre del negocio del usuario. Para ello usaremos EdiTextPreference:

Ejemplo EditTextPreference en AndroidX

EditTextPreference muestra un EditText en un diálogo y guarda como String el valor ingresado por el usuario.

Para implementarla, solo abre el archivo res/xml/preferences.xml y añade una etiqueta <EditTextPreference>:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <EditTextPreference
        app:defaultValue="No establecido"
        app:key="businessName"
        app:title="Nombre del negocio"
        app:useSimpleSummaryProvider="true" />
</PreferenceScreen>

Los atributos usados cumplen con las siguientes configuraciones:

  • defaultValue: Valor por defecto que será asignado a la preferencia si no tiene almacenado uno previo
  • useSimpleSummaryProvider: Determina si la preferencia debería cambiar su atributo summary por el valor confirmado por el usuario

ListPreference

Este tipo de preferencia te provee un patrón de selección única entre varias entradas. Nuestra App aprovechará esta clase para permitir al usuario elegir su moneda:

Ejemplo de ListPreference En Android

ListPreference muestra una lista de RadioButtons en un diálogo, del cual el usuario puede elegir solo uno.

Para disponer la selección de la moneda del usuario añade una etiqueta <ListPreference> en la jerarquía de preferencias de esta forma:

<ListPreference
    app:defaultValue="1"
    app:entries="@array/currency_entries"
    app:entryValues="@array/currency_values"
    app:key="currency"
    app:title="Moneda"
    app:useSimpleSummaryProvider="true" />

Lo especial de este tipo de preferencia son los atributos entries y entryValues. El primero es un array con los valores que se mostrarán al usuario y el segundo un array de los índices que les corresponden:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="currency_entries">
        <item>COP</item>
        <item>USD</item>
        <item>EUR</item>
    </string-array>

    <string-array name="currency_values">
        <item>1</item>
        <item>2</item>
        <item>3</item>
</resources>

Dicha correspondencia se ve en la selección, si selecciona la etiqueta "COP" se tomará el índice "1" como valor actual de la preferencia. Obviamente, ambos arreglos deben ser del mismo tamaño para sostener esta estructura 1:1.


MultiSelectListPreference

Ahora bien, supongamos que deseamos establecer una frecuencia de envío de un resumen de cuenta al correo del usuario. Aquí entraría MultiSelectListPreference:

Ejemplo MultiSelectListPreference en AndroidX

Esta despliega una lista de CheckBoxes en un diálogo y el usuario puede elegir múltiples entradas.

Al igual que ListPreference, añade una etiqueta <MultiSelectListPreference>:

<MultiSelectListPreference
    android:defaultValue="@array/account_summary_default"
    android:entries="@array/account_summary_entries"
    android:entryValues="@array/account_summary_values"
    android:key="accountSummary"
    android:title="Frecuencia resumen de cuenta" />

Uusaremos entries y entryValues para presentar a los días de la semana:

<string-array name="account_summary_entries">
    <item>Lunes</item>
    <item>Martes</item>
    <item>Miércoles</item>
    <item>Jueves</item>
    <item>Viernes</item>
    <item>Sábado</item>
    <item>Domingo</item>
</string-array>

<string-array name="account_summary_values">
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>6</item>
<item>7</item>
</string-array>

Como ves, el texto secundario no es actualizado al confirmar las entradas seleccionadas. Por lo que debemos hacerlo desde Kotlin:

private fun setUpAccountSummary() {
    val accountSummary: MultiSelectListPreference? = findPreference("accountSummary")
    accountSummary?.run {
        setSummary(values) // Texto secundario inicial
        setOnPreferenceChangeListener { _, newValue ->
            val newValues = newValue as Set<String>
            accountSummary.setSummary(newValues) // Texto secundario al cambiar
            true
        }
    }
}

fun MultiSelectListPreference.setSummary(newValues: Set<String>) {
    summary = if (newValues.isEmpty())
        "Sin envío"
    else
        newValues.joinToString(", ") { value ->
            entries[findIndexOfValue(value)]
        }
}

Desde SettingsFragment configuramos la preferencia accountSummary a partir de una función de extensión llamada setSummary().

Cambiar summary de MultiSelectListPreference en AndroidX

Esta consiste en la evaluación de los valores seleccionados. Si no hay ninguno, entonces disponemos del texto "Sin envío". De lo contrario usamos la función joinToString() para mapear los valores en un String que contenga la lista de entradas separadas por coma.

Encontrar el índice de una entrada a partir del valor lo logras con findIndexOfValue().

Finalmente desde setUpAccountSummary() usamos la función de alcance run() para crear un contexto de configuración de la preferencia. Claramente buscamos actualizar el valor de Preference.summary, por lo que realizamos la asignación inicial y luego cuando el observador OnPreferenceChangeListener detecte un nuevo cambio.


SeekBarPreference

Cuando se da la situación en la que debes proveer al usuario la selección entre un conjunto de valores continuos usa a SeekBarPreference. Por ejemplo, proveer un rango de distancia para filtrar ofertas:

Ejemplo de SeekBarPreference en AndroidX

Como ves, SeekBarPreference usa en su layout una SeekBar junto a un TextView que muestra el valor actual del deslizador.

Concretamente en nuestro ejemplo, añadimos la etiqueta <SeekBarPreference> para la distancia así:

<SeekBarPreference
    app:defaultValue="50"
    app:showSeekBarValue="true"
    app:title="Distancia (KM)" />

Los atributos específicos de este componente son:

  • min, max: Valores mínimo y máximo del rango
  • showSeekBarValue: Determina si se mostrará el texto con el valor actual de la barra
  • updatesContinuously: Indica si se debe persistir el valor de la SeekBar continuamente

SwitchPreferenceCompat

La SwitchPreferenceCompat provee dos estados con el widget Switch. Este formato te permite otorgarle al usuario la activación/desactivación de un comportamiento o presentación en la App. Por ejemplo, habilitar la visualización de una sección de últimos cambios:

Ejemplo de SwitchPreferenceCompat en AndroidX

Probemos su estética y comportamiento añadiendo una nueva etiqueta <SwitchPreferenceCompat> en nuestra jerarquía:

<SwitchPreferenceCompat
    android:defaultValue="true"
    android:key="latestEvents"
    android:title="Mostrar últimos cambios"
    app:icon="@drawable/ic_timeline"
    app:summaryOff="No aparecerán los últimos cambios ocurridos en la pantalla principal"
    app:summaryOn="Aparecerán los últimos cambios ocurridos en la pantalla principal" />

Ya que SwitchPreferenceCompat es heredera de TwoStatePreference, tendrás los siguientes atributos a disposición:

  • summaryOn: Texto secundario que aparece en estado activo del control
  • summaryOff: Texto secundario para el estado inactivo

Dependencia De Ajustes

Si deseas que el valor de un ajuste dependa del de otro que usa SwitchPreferenceCompat, entonces usa el atributo dependency. Por ejemplo, si la aparición de los últimos cambios está activa, podemos habilitar una preferencia que defina el atributo de ordenamiento de dicha sección:

Ejemplo de app:dependency con SwitchPreferenceCompat en AndroidX

Para establecer la dependencia asigna el valor de app:key de la preferencia SwitchPreferenceCompat en el atributo app:dependency de la preferencia dependiente:

<ListPreference
    android:defaultValue="date"
    android:entries="@array/latest_events_order_entries"
    android:entryValues="@array/latest_events_order_values"
    android:key="latestEventsOrder"
    android:title="Ordenar por"
    app:dependency="latestEvents"
    app:useSimpleSummaryProvider="true" />

Como ves, asignamos latestEvents en una nueva preferencia que define el atributo de ordenamiento para los cambios registrados como eventos

Esto hará que al desactivar el valor del switch, se desactive el ajuste asociado de ordenamiento.


Organizar Preferencias

En este tutorial aprendiste sobre las clases disponibles para componer una pantalla de ajustes con múltiples patrones de selección y colección de datos del usuario.

No obstante, el estado actual muestra múltiples aspectos revueltos que podrían ser separados por categorías. Y es precisamente esta organización la que aplicaremos en el siguiente tutorial Categorías Y Subpantallas De Preferencias (todo).

Veremos cómo usar la clase PreferenceCategory para separar por grupos a nuestras preferencias. O como agregar subpantallas de preferencias para marcar aún más la separación.


Más Contenidos Android

Únete Al Discord De Develou

Si tienes problemas con el código de este tutorial, preguntas, recomendaciones o solo deseas discutir sobre desarrollo Android conmigo y otros desarrolladores, únete a la comunidad de Discord de Develou y siéntete libre de participar como gustes. ¡Te espero!