El primer momento para diseñar una pantalla de ajustes en tus Apps Android se da al crear el fragmento de preferencias que muestre la jerarquía de opciones.
Por lo que en este tutorial aprenderás a:
- Crear una jerarquía de preferencias en XML y en Kotlin
- Inflar la jerarquía sobre el fragmento de tu pantalla de preferencias
- Obtener la referencia de una preferencia
- Procesar clicks sobre una preferencia
Ejemplo Para Crear Fragmento De Preferencias
En este tutorial crearemos el proyecto base en Android Studio que estudiaremos a lo largo de la guía de preferencias (todo):
Ya que es la introducción, la interfaz es sencilla. Tendremos una sola preferencia asociada al ajuste de la versión de compilación de nuestra App. Puedes descargar el código desde aquí:
Con esto en mente, veamos el contexto de componentes a usar.
Fragmento De Preferencias
La clase PreferenceFragmentCompat
es el punto de entrada para usar las características de la librería AndroidX Preferences. Por lo que los elementos de UI, acciones y almacenamiento de los valores de preferencias estarán ligados a este elemento.
Cada preferencia es materializada por la clase Preference
y se ubica al interior de un elemento PreferenceScreen
(u concepto similar a la construcción de layouts). Al construir la jerarquía, pasamos a inflarla sobre el fragmento.
Organizando estas ideas, nuestro plan será el siguiente:
- Crear proyecto Android Studio con una actividad principal
- Añadir un menú de opciones para iniciar los ajustes
- Crear actividad de ajustes
- Crear la jerarquía vía XML
- Inflar jerarquía sobre fragmento
Una vez construida la interfaz, pasaremos a manejar los eventos sobre la preferencia.
Sabiendo los pasos a seguir, comencemos a implementarlos:
1. Crear Proyecto En Android Studio
Abre Android Studio y crea un nuevo proyecto basado en la plantilla Empty Activity para generar una actividad inicial vacía.
Debido a que usaremos la librería de preferencias, añadiremos la siguiente dependencia en el archivo build.gradle
del módulo con la última versión:
dependencies {
// ...
implementation "androidx.preference:preference-ktx:ultima_version"
}
2. Crear Opción De Ajustes En Menú
Lo siguiente será añadir un nuevo recurso de menú con un solo ítem de acción para navegar hacia la futura actividad de ajustes.
Así que añade un nuevo archivo a la carpeta res/menu
con el nombre de main_menu.xml
y luego crea una etiqueta <item>
en su interior:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/settings"
android:title="@string/settings" />
</menu>
Luego abre la actividad principal y sobrescribe los controladores de inflado y procesamiento de clicks para el menú de opciones:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.main_menu, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (R.id.settings == item.itemId) {
toSettings()
return true
}
return super.onOptionsItemSelected(item)
}
private fun toSettings() {
val intent = Intent(this, SettingsActivity::class.java)
startActivity(intent)
}
}
Evidentemente SettingsActivity
aún no existe, por lo que crearla será nuestro siguiente paso.
3. Crear Actividad De Ajustes
Desde tu paquete haz click derecho y selecciona New>Activity>Empty Activity
. Usa como nombre SettingsActivity
.
En seguida, abre su layout activity_settings.xml
y asígnale a su único nodo el id settings_container
:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/settings_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SettingsActivity">
</androidx.constraintlayout.widget.ConstraintLayout>
Recuerda que necesitaremos un contenedor identificable para poder realizar la transacción del fragmento de ajustes más adelante.
Si ejecutas la aplicación podrás navegar desde el botón de acción que se despliega desde el overflow en la app bar.
Con estos componentes construidos, pasemos a incluir las instrucciones sobre las preferencias.
4. Crear Jerarquía De Preferencias
Añade un nuevo archivo llamado preferences.xml
al directorio res/xml
. Indica en la ventana de creación, que deseas que el nodo padre sea un elemento PreferenceScreen:
Una vez creado, ya tienes el lienzo para incluir tantos elementos Preference
(o sus descendientes) como desees.
En nuestro caso concreto, comenzaremos añadiendo una sola etiqueta <Preference>
por simplicidad:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
<Preference
app:key="buildVersion"
app:summary="1.0.0"
app:title="Versión de compilación" />
</PreferenceScreen>
La clase Preference
es el bloque de construcción básico de una jerarquía de preferencias. En esta forma solo consiste de un texto principal y su resumen:
Los atributos base usados tienen el siguiente propósito:
app:key
: Representa la clave de la preferencia para almacenar su valorapp:title
: Título de la preferencia. Expone el significado de la opciónapp:summary
: Es el texto secundario que aparece por debajo del título. Su propósito es brindar información sobre el estado actual de la preferencia
5. Inflar Jerarquía De Preferencias
Aquí entre en juego la creación de un fragmento que extienda de PreferenceFragmentCompat
.
A diferencia de la clase Fragment
, sobrescribiremos el controlador onCreatePreferences()
para inflar el archivo preferences.xml
con el método setPreferencesFromResource()
:
class SettingsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.preferences, rootKey)
}
}
El método onCreatePreferences()
es llamado desde onCreate()
del fragmento para inflar la interfaz. Por esta razón recibe como primer parámetro el estado anterior si el fragmento se está recreando.
rootKey
es el atributo android:key
del elemento PreferenceScreen
del recurso XML. Si no es null
, es usado para asociar el fragmento a dicho elemento.
Finalizamos la actividad de ajustes, instalando el fragmento con una transacción a través del manejador de fragmentos:
class SettingsActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_settings)
if (savedInstanceState == null) {
supportFragmentManager
.beginTransaction()
.replace(R.id.settings_container, SettingsFragment())
.commit()
}
supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
}
Nota: Es posible crear el fragmento de preferencias automáticamente yendo a New>Fragment>Settings Fragment. Android Studio creará la clase Kotlin y el archivo XML con la jerarquía por ti.
6. Encontrar Una Preferencia
Supongamos que deseas modificar el título de la preferencia de versión en tiempo de ejecución.
¿Cómo accedes a esta?
Al igual que con los views, puedes obtener la referencia de una preferencia en la jerarquía, a través del método findPreference()
y su clave:
class SettingsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.preferences, rootKey)
val buildVersion: Preference? = findPreference("buildVersion")
buildVersion?.summary = BuildConfig.VERSION_NAME
}
}
Una vez conseguido el objeto, simplemente accedes a sus propiedades para el cambio.
En el ejemplo concreto del título será Preference.summary
, donde asignamos la constante VERSION_NAME
producida por la construcción del proyecto.
7. Manejar Eventos De Click En Preferencia
Existen dos formas de establecer las acciones que se ejecutan cuando una preferencia es tocada: añadir un Intent o una escucha OnPreferenceClickListener
.
Veamos cómo aplicar ambos casos:
Asignar Intent A Preferencia
Puedes especificar un intent para la preferencia tanto desde la definición XML como de su propiedad Kotlin.
Por ejemplo, supongamos que deseamos iniciar un navegador que muestre la URL de la página de ayuda asociada a nuestra preferencia:
<?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">
<Preference
app:key="buildVersion"
app:summary="1.0.0"
app:title="Versión de compilación">
<intent
android:action="android.intent.action.VIEW"
android:data="http://www.develou.com/fragmento-de-preferencias-androidx" />
</Preference>
</PreferenceScreen>
Hacerlo consiste en crear una etiqueta <intent>
al interior de la preferencia. Luego usa la acción VIEW
en android:action
y la URL objetivo en android:data
.
Por otra parte, el caso equivalente desde Kotlin requiere que obtengas la preferencia y asignarle el Intent previamente construido a Preference.intent
:
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("http://www.develou.com")
buildVersion?.intent = intent
Asignar Escucha OnPreferenceClickListener
Adicionalmente puedes usar a OnPreferenceClickListener
para recibir el consumo de eventos desde su método onPreferenceClick()
:
buildVersion?.setOnPreferenceClickListener {
Log.d("SettingsFragment", "\"${it.title}\" fue clickeada")
false
}
A diferencia de asignar un intent, esta escucha te da libertad para ejecutar cualquier tipo de lógica en tiempo de ejecución. Retornas true
para especificar si el click fue manejado o false
en caso contrario.
El código anterior despliega el siguiente mensaje en el Logcat:
D/SettingsFragment: "Versión de compilación" fue clickeada
8. Crear Preferencias Desde Kotlin
Por otro lado, si deseas construir en tiempo de ejecución la jerarquía con Kotlin, entonces realiza la composición en el método onCreatePreferences()
del fragmento:
class SettingsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
val context = preferenceManager.context
val screen = preferenceManager.createPreferenceScreen(context)
val buildVersionPreference = Preference(context).apply {
key = "buildVersion"
setTitle(R.string.build_version_preference)
summary = BuildConfig.VERSION_NAME
}
screen.addPreference(buildVersionPreference)
preferenceScreen = screen
}
}
La creación se resume a:
- Crear un componente
PreferenceScreen
con la clase de utilidadPreferenceManager
- Crear preferencia o categoría (todo) desde su constructor. Puedes usar
apply()
para configurar los atributos deseados. - Añadir la preferencia a la pantalla con
addPreference()
- Asignar la jerarquía a la propiedad
PreferenceFragmentCompat.preferenceScreen
Plantilla Settings Activity En Android Studio
A pesar de que construimos manualmente nuestra actividad de ajustes junto al fragmento, puedes ahorrarte estos pasos de construcción con la plantilla Settings Activity
.
Úsala desde la creación de un nuevo proyecto o desde New>Activity>Settings Activity.
Android Studio generará la actividad, el fragmento y el recurso XML con una jerarquía de ejemplo.
Tipos De Preferencias
En este tutorial viste el primer paso para crear tu pantalla de preferencias: crear el PreferenceFragmentCompat
. Aprendiste a cómo crear la jerarquía de preferencias e inflarla. Además de obtener las preferencias desde el código para modificar sus atributos y procesar acciones de click.
No obstante, este ejemplo es muy básico en el estado en que está. Avanza al siguiente tutorial Tipos De Preferencias (todo) donde verás las diferentes clases que existen para representar diferentes patrones como la selección única, selección múltiple, diálogos, etc.
Más Contenidos Android
- Actividad de preferencias con android.preference
- Todos los tutorial de desarrollo Android
- Cursos online de desarrollo 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!