Valores De Preferencias Con SharedPreferences

ANUNCIO
Loading...

Ahora es el turno de usar los valores almacenados de nuestras preferencias con SharedPreferences, a partir de la jerarquía de ajustes que hemos construido con la librería androidx.preference.

Valores de preferencias con SharedPreferences

Por lo que en este tutorial aprenderás a leer valores almacenados de preferencias y a escuchar eventos de cambios de los mismos.

Nota: Este tutorial es la cuarta parte de la guía de la librería de preferencias, por lo que asumiré que leíste la parte tres Categorías De Preferencias y sus antecesores.


Ejemplo De Preferencias Con SharedPreferences

Para incluir en nuestra App de ejemplo la lectura de los valores de las preferencias, añadiremos varios TextViews que permitan mostrar el estado actual del almacenamiento para los ajustes del usuario:

App de ejemplo para mostrar valores de preferencias con SharedPreferences

También ejemplificaremos como escuchar el cambio de una preferencia, antes de que se guarde para procesar su contenido:

Ejemplo de OnPreferenceChangeListener en Android

Y finalizaremos escuchando el cambio de cualquier valor luego de que fue confirmado.

Ejemplo de OnSharePreferenceChangeListener en Android

Puedes descargar el proyecto Android Studio desde el siguiente enlace. Abre el módulo P4_Preferencias_Con_SharedPreferences que será el correspondiente al desarrollo de estos ejemplos.

Con esto en mente, comencemos por la lectura de preferencias.


1. Leer Valores De Preferencias

La librería de preferencias maneja internamente el guardado de los valores en SharedPreferences. Por esta razón es posible pasar directamente a la lectura de dichos elementos.

SharedPreferences almacena a cada preferencia como un par clave-valor, donde la clave es el string que asignaste en el atributo app:key en preferences.xml o a la propiedad Preference.key si creaste la preferencia dinámicamente.

Por ejemplo, para nuestra preferencia del nombre del negocio:

<EditTextPreference app:defaultValue="No establecido" app:key="businessName" app:title="Nombre del negocio" app:useSimpleSummaryProvider="true" />
Lenguaje del código: HTML, XML (xml)

El par sería "businessName"-"TextoDelUsuario" dependiendo de qué entrada sea colectada por el diálogo.

Por lo que si quieres leer el valor almacenado, entonces obtienes la referencia al punto de acceso con PreferenceManager.getDefaultSharedPreferences() y luego invocas el método get*() asociado al tipo de la preferencia:

PreferenceManager.getDefaultSharedPreferences(this).getString("businessName", "")
Lenguaje del código: Kotlin (kotlin)

Ya que businessName se relaciona con un valor de texto, el método a llamar es getString().

Como ves, la dinámica de lectura es muy sencilla. Así que repliquemos esta implementación para mostrar a los valores de nuestras preferencias en MainActivity.

Diseñar Layout

Layout para mostrar valores de preferencias

En primera instancia actualizaremos al archivo activity_main.xml para incluir un TextView para las siguientes preferencias:

  • Cuenta
    • Nombre del negocio
  • Ubicación
    • Moneda
    • Distancia mínima de ofertas
  • Notificaciones
    • Días en que se envía resumen de cuenta
  • Presentación
    • Mostrar sección de últimos cambios
    • Tema
    • Mostrar animaciones
    • Diseño de colecciones

Puedes simplificar el diseño usando un LinearLayout vertical de la siguiente forma:

<?xml version="1.0" encoding="utf-8"?> <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" android:padding="16dp" tools:context=".MainActivity"> <TextView android:id="@+id/account" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingTop="16dp" android:text="Cuenta" android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1" /> <TextView android:id="@+id/business_name" android:layout_width="wrap_content" android:layout_height="wrap_content" tools:text="Nombre del negocio = Abogados PQA" /> <TextView android:id="@+id/locale" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="16dp" android:text="Ubicación" android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1" /> <TextView android:id="@+id/currency" android:layout_width="match_parent" android:layout_height="wrap_content" tools:text="Moneda = COP" /> <TextView android:id="@+id/distance" android:layout_width="match_parent" android:layout_height="wrap_content" tools:text="Distancia mínima de ofertas = 75km." /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="16dp" android:text="Notificaciones" android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1" /> <TextView android:id="@+id/account_summary" android:layout_width="match_parent" android:layout_height="wrap_content" tools:text="Días en que se envía resumen de cuenta = [1, 3, 5]" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="16dp" android:text="Presentación" android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1" /> <TextView android:id="@+id/latest_events" android:layout_width="match_parent" android:layout_height="wrap_content" tools:text="Mostrar sección de últimos cambios = True\nY Ordenar por = date" /> <TextView android:id="@+id/theme" android:layout_width="match_parent" android:layout_height="wrap_content" tools:text="Tema = Claro" /> <TextView android:id="@+id/animations" android:layout_width="match_parent" android:layout_height="wrap_content" tools:text="Mostrar animaciones = false" /> <TextView android:id="@+id/collections_design" android:layout_width="match_parent" android:layout_height="wrap_content" tools:text="Diseño de colecciones = Grilla" /> </LinearLayout>
Lenguaje del código: HTML, XML (xml)

Mostrar Valores De Preferencias

Ahora desde onCreate() de MainActivity, vamos a obtener la referencia de cada TextView y le asignamos a su propiedad text el valor de cada preferencia.

class MainActivity : AppCompatActivity() { private lateinit var businessName: TextView private lateinit var currency: TextView private lateinit var distance: TextView private lateinit var accountSummary: TextView private lateinit var latestEvents: TextView private lateinit var themeText: TextView private lateinit var animations: TextView private lateinit var collectionsDesign: TextView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setUpUI() loadPrefs() } private fun setUpUI() { businessName = findViewById(R.id.business_name) currency = findViewById(R.id.currency) distance = findViewById(R.id.distance) accountSummary = findViewById(R.id.account_summary) latestEvents = findViewById(R.id.latest_events) themeText = findViewById(R.id.theme) animations = findViewById(R.id.animations) collectionsDesign = findViewById(R.id.collections_design) } private fun loadPrefs() { PreferenceManager.getDefaultSharedPreferences(this).let { store -> businessName.text = getString( R.string.business_name, store.getString("businessName", "") ) currency.text = getString( R.string.currency, store.getString("currency", "") ) distance.text = getString( R.string.distance, store.getInt("distance", 0) ) accountSummary.text = getString( R.string.account_summary, store.getStringSet("accountSummary", emptySet()) ) val eventsActive = store.getBoolean("latestEvents", false) var string = getString(R.string.latest_event, eventsActive) if (eventsActive) string += getString( R.string.latest_events_order, store.getString("latestEventsOrder", "") ) latestEvents.text = string themeText.text = getString( R.string.theme, store.getString("theme", "Claro") ) animations.text = getString( R.string.animations, store.getBoolean("animations", false) ) collectionsDesign.text = getString( R.string.collections_design, store.getString("collectionsDesign", "Lista") ) } } //... }
Lenguaje del código: Kotlin (kotlin)

La lectura es una tarea repetitiva, por lo que podemos cubrirla con let() para mapear la población de views. Es importante destacar el método get*() usado para cada subclase:

  • Preference, EditTextPreference, ListPreference -> getString()
  • SwitchPreferenceCompat, CheckBoxPreference -> getBoolean()
  • SeekBarPreference -> getInt()
  • MultiSelectListPreference -> getStringSet()

Al desplegar los valores en SharedPreferences verías algo así:

Preferencias con SharedPreferences

Recuerda que el método Context.getString() obtiene un recurso string del archivo strings.xml. Debido a que usamos elementos con argumentos, pasamos como segundo argumento el valor obtenido de las preferencias para reemplazar el placeholder:

<!-- Strings para lectura de valores de preferencias--> <string name="business_name">Nombre del negocio = %1$s </string> <string name="currency">Moneda = %1$s</string> <string name="distance">Distancia mínima de ofertas = %1$dkm.</string> <string name="account_summary">Días en que se envía resumen de cuenta = %1$s</string> <string name="latest_event">Mostrar sección de últimos cambios = %1$b</string> <string name="latest_events_order">\nY ordenar por = %1$s</string> <string name="theme">Tema = %1$s</string> <string name="animations">Animations = %1$b</string> <string name="collections_design">Diseño de colecciones = %1$s</string>
Lenguaje del código: HTML, XML (xml)

2. Escuchar Cambios Antes De Guardar Preferencia

Si deseas obtener el control en el momento en que se intenta cambiar el valor de una preferencia específica, entonces usa la interfaz OnPreferenceChangeListener y su controlador onPreferenceChange().

Este método recibe la preferencia que se cambiará y el nuevo valor para dicho cambio. Ya habíamos usado este observador en Tipos de preferencias, cuando actualizábamos el texto secundario de una MultiSelectListPreference:

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 } } }
Lenguaje del código: Kotlin (kotlin)

Pero veamos otro ejemplo donde validamos que el nombre del negocio tenga por lo menos diez caracteres.

Esta acción consiste en comparar la propiedad String.lenght con el literal 10. Si cumple con la precondición, entonces retornamos true, de lo contrario false:

private fun setUpBusinessName() { val businessName = findPreference<EditTextPreference>("businessName") businessName?.setOnPreferenceChangeListener { _, newValue -> if (newValue.toString().length >= 10) true else { Toast.makeText( requireActivity(), "El nombre debe tener más de 10 caracteres", Toast.LENGTH_SHORT ).show() false } } }
Lenguaje del código: Kotlin (kotlin)

true indica que el valor se almacena y false evita que se guarde el nuevo valor entrante.

Toast mostrado antes de intentar cambiar valor de preferencia

Nota: Asignar la anterior escucha protege a la preferencia de valores que no deseamos, pero no avisa al usuario del por qué. Una de las soluciones para ello es que implementes tu propia EditTextPreference para mostrar el error en el EditText con setError().


3. Escuchar Cambios Después De Guardar Preferencia

Por otro lado, es posible que requieras manejar el cambio pero luego de que ha sido guardado el valor de alguna preferencia.

Para ello existe la interfaz SharedPreferences.OnSharedPreferenceChangeListener. A diferencia de OnPreferenceChangeListener, esta percibe los cambios en todas las preferencias.

Veamos un ejemplo simplificado para ilustrar la implementación de esta escucha. Registremos un nuevo observador que muestre un Toast que presente la preferencia que cambió y su valor asentado:

Toast después de cambiar valor de preferencia

Ve a SettingsFragment e implementale a OnSharedPreferenceChangeListener y luego sobrescribe al método onSharedPreferenceChanged():

class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener { //... override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { TODO("Not yet implemented") } }
Lenguaje del código: Kotlin (kotlin)

Luego vincula al fragmento como observador a las preferencias a través de registerOnSharedPreferenceChangeListener() en onResume() para que perciba los eventos cuando el fragmento está listo para interactuar con el usuario.

override fun onResume() { super.onResume() preferenceManager.sharedPreferences.registerOnSharedPreferenceChangeListener(this) }
Lenguaje del código: Kotlin (kotlin)

Equivalentemente, cancela el recibimiento de cambios cuando el fragmento entra en pausa:

override fun onPause() { super.onPause() preferenceManager.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this) }
Lenguaje del código: Kotlin (kotlin)

Y por último escribimos la lógica de onSharedPreferenceChanged():

override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { Toast.makeText( requireActivity(), "Preferencia cambiada: $key", Toast.LENGTH_SHORT ).show() }
Lenguaje del código: Kotlin (kotlin)

Al recibir la instancia de SharedPreferences y la clave de la preferencia, es posible realizar una comparación con la clave que desees procesar. De esta forma puedes obtener el valor nuevo con algún método get*() e iniciar el cambio en la capa respectiva de tu App.


Valores De Preferencias Con PreferenceDataStore

En este tutorial aprendiste a leer los valores de tus preferencias a partir de la API SharedPreferences. Además, viste ejemplos para escuchar los cambios en los valores antes y después de la confirmación del usuario.

Aunque este almacén es la fuente de datos por defecto de la librería de preferencias de AndroidX, existe una clase llamada PreferenceDataStore que te permite personalizar el lugar donde almacenarás los valores de tus preferencias.

Por esta razón, en el siguiente tutorial veremos cómo implementar una fuente de preferencias personalizada (todo) que sea de utilidad por si SharedPreferences no cumple con nuestras necesidades.


Más Contenidos Android

¿Ha sido útil esta publicación?