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.
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:
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:
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
:
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 previouseSimpleSummaryProvider
: Determina si la preferencia debería cambiar su atributosummary
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:
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
:
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()
.
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:
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 rangoshowSeekBarValue
: Determina si se mostrará el texto con el valor actual de la barraupdatesContinuously
: 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:
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 controlsummaryOff
: 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:
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!