Paramètres utilisateur dans l'app. Kotlin pour Android

Les fichiers de préférences, SharedPreferences, correspondent aux fichiers de paramètres de l’application. Ils sont utilisés pour sauvegarder des données persistantes (disponible après la fermeture de l’application). C’est la première solution à envisager lorsqu’on souhaite sauvegarder des données.

En particulier, ces fichiers de préférences permettent de stocker des données sous la forme de couple ( clé, valeur ) où :

  • clé est de type String, permettant de référencer la valeur,
  • valeur est une donnée primitive telle qu’un Integer, un Boolean, ou un String.

Ces couples sont consultables durant l’exécution et persistent après la fermeture de l’application. Un des intérêts est de pouvoir y accéder depuis n’importe quelle classe ayant accès au contexte de l’application, notamment depuis une Activity ou un Fragment.

Par exemple, pour une application destinée à un seul utilisateur, il serait intéressant d’y stocker le profil de ce dernier (préférence de son, âge, sexe, etc.).

Cet article détaille comment créer un écran de paramètre, permettant d’enregistrer le paramètre audio de l’utilisateur.

Ensuite, il s’agit de récupérer la valeur de ce paramètre, à partir de fonctions natives du SDK Android.

Puis, il est élaboré d’écrire dans un fichier de préférence, sans passer par une interface utilisateur.

Enfin, il est utilisé une classe déléguée Kotlin afin de simplifier l’utilisation des SharedPreferences.

Ajouter un écran de paramètre

Tout d’abord, il s’agit de créer la vue XML spécifique aux préférences, Preferences [1]. Ensuite, cette vue devra être liée à une classe héritant de PreferencesFragment.

  1. Ajoutez un dossier de ressource dans le dossier res/ via Android Studio, spécifiez xml pour le type de dossier, de façon à le nommer xml/.
  2. Ajoutez un fichier XML fragment_settings.xml dans le dossier venant d’être créé :

     <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
     </PreferenceScreen>
    
  3. Ajoutez un bouton à bascule, à l’intérieur des balises <PreferenceScreen>. Il représente le paramètre audio, avec comme clé, @string/pref_sound et valeur par défaut, true :

     <SwitchPreference
     android:defaultValue="true"
     android:key="@string/pref_sound"
     android:title="@string/label_sound" />
    
  4. Importer la bibliothèque de support de préférence dans votre projet via le fichier gradle du module build.gradle (Module: app) :

     implementation "androidx.preference:preference-ktx:1.1.1"
    
  5. Ajoutez une nouvelle classe Kotlin, SettingsFragment, elle hérite de PreferencesFragmentCompat :

     class SettingsFragment : PreferenceFragmentCompat() {
          override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
    
         }
     }
    
  6. Chargez le fichier de vue fragment_settings.xml dans SettingsFragment, via la fonction addPreferencesFromResource(), à appeler depuis la méthode onCreatePreferences() :

     addPreferencesFromResource(R.xml.fragment_settings)
    
  7. Chargez la valeur entrée par défaut dans fragment_settings.xml dans le fichier SharedPreferences de défaut, depuis l’ Activity principale :

     PreferenceManager.setDefaultValues(this, R.xml.settings, false)
    

Remarque : Pour gerer, le style de l’écran, de préférence, pour les versions supérieur à l’API 21, via le fichier styles.xml(v-21), dans le thème de l’Activity principale :

    <item name="preferenceTheme">@style/PreferenceThemeOverlay</item>

À présent, le fichier SharedPreferences de défaut contient un couple de clé, valeur (@string/pref_sound, true). La valeur associée à la clé @string/pref_sound sera automatiquement mise à jour en fonction des interactions de l’utilisateur avec cet écran de paramètre.

Une alternative est d’utiliser la bibliothèque Data Store de Jetpack. Retrouvez le tutoriel dans le thème “Paramètres Utilisateur” de l’application “Kotlin pour Android”.

Paramètres utilisateur avec Data store dans l'app. Kotlin pour Android

Lire le fichier de préférence de défaut

Afin de récupérer la valeur précédemment stockée, il faut cibler le fichier SharedPreferences de défaut. Ensuite, il s’agit de récupérer la valeur via la fonction de lecture appropriée (getInt(), getBoolean() ou getString(), etc.), cela depuis une Activity ou un Fragment.

  1. Récupérez le fichier SharedPreferences de défaut, dans une variable defaultSharedPref :

     var defaultSharedPref = PreferenceManager.getDefaultSharedPreferences(this)
    
  2. Récupérez la valeur associée à la clé @string/pref_sound :

     defaultSharedPref.getBoolean(getString(R.string.pref_sound), true)
    

D’une part, les fichiers de préférences peuvent être ouvert en lecture afin d’y récupérer les données préalablement enregistrées. D’autre part, ces fichiers peuvent être ouvert en écriture (ou édition).

Écrire dans un des fichiers de préférence

Afin d’écrire dans un fichier de préférence, il s’agit de cibler un des fichiers, puis de récupérer son éditeur [3]. A ce moment, il est possible de modifier, ajouter ou supprimer des couples de clé, valeur.

  1. Ciblez un fichier de préférence, sans nom, dans un mode privé (seule votre application y aura accès) :

     var sharedPref = getPreferences(Context.MODE_PRIVATE)
    
  2. Récupérez l’éditeur :

     var editor: SharedPreferences.Editor = sharedPref.edit()
    
  3. Insérez un nombre :

     editor.putInt(PREF_NB, nb)
    

    avec companion object { const val PREF_NB = "PREF_NB" } une constante de type String.

  4. Validez le changement apporté au fichier :

     editor.commit()
    

Il est également possible de nommer un fichier de préférence :

var sharedPref = getPreferences("user", Context.MODE_PRIVATE)

De plus, il est possible d’autoriser l’accès d’un fichier de préférence à d’autres applications :

var sharedPref = getPreferences(Context.MODE_WORLD_WRITEABLE)

Par ailleurs, à l’instar des fonctions de lecture, il y a les fonctions d’écriture, selon le type pris par la valeur (putInt(), putBoolean() ou putString(), etc.).

Plus loin avec Kotlin

De façon à faciliter l’ajout, la modification et la lecture de couple clé, valeur, il est intéressant d’utiliser une classe déléguée Kotlin.

  1. Ajoutez un fichier Kotlin DelegatesExt :

     object DelegatesExt {
         fun <T> preference(context: Context, name: String,
                    default: T) = Preference(context, name, default)
         fun <T> preference(fragment: Fragment, name: String,
                    default: T) = Preference(fragment, name, default)
     }
    
  2. Dans ce fichier [5], à la suite, ajoutez la classe avec un type réifié Preference<T> :

     class Preference<T>(private var context: Context?, private val name: String,
                 private val default: T) {
    
             var myFragment: Fragment? = null
    
             constructor(fragment: Fragment, name: String, default: T) : this(null, name, default) {
                     myFragment = fragment
             }
    
             private val prefs: SharedPreferences by lazy {
                     if (myFragment == null)
                     PreferenceManager.getDefaultSharedPreferences(context)
                     else
                     PreferenceManager.getDefaultSharedPreferences(myFragment!!.activity)
             }
    
             operator fun getValue(thisRef: Any?, property: KProperty<*>): T = findPreference(name, default)
    
             operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
                     putPreference(name, value)
             }
    
             @Suppress("UNCHECKED_CAST")
             private fun findPreference(name: String, default: T): T = with(prefs) {
                     val res: Any = when (default) {
                     is Long -> getLong(name, default)
                     is String -> getString(name, default) as Any
                     is Int -> getInt(name, default)
                     is Boolean -> getBoolean(name, default)
                     is Float -> getFloat(name, default)
                     else -> throw IllegalArgumentException("This type can be saved into Preferences")
                     }
                     res as T
             }
    
             @SuppressLint("CommitPrefEdits")
             private fun putPreference(name: String, value: T) = with(prefs.edit()) {
                     when (value) {
                     is Long -> putLong(name, value)
                     is String -> putString(name, value)
                     is Int -> putInt(name, value)
                     is Boolean -> putBoolean(name, value)
                     is Float -> putFloat(name, value)
                     else -> throw IllegalArgumentException("This type can't be saved into Preferences")
                     }.apply()
             }
     }
    
  3. Utilisez cette classe déléguée, depuis une Activity ou un Fragment, en déclarant une variable (représentant le nom de l’utilisateur, par exemple) :

     var userName: String by DelegatesExt.preference(this, PREF_NAME, "John")
    
  4. Créez la variable statique dans l’Activity principale, via un companion object :

     companion object {
             const val PREF_NAME = "NAME"
     }
    
  5. Modifiez cette variable userName, cela aura pour effet de modifier le fichier SharedPreferences de défaut :

     userName = "Macha"
    

Finalement, cet article présente l’utilisation des fichiers de préférence pour des paramètres nécessitant une interface utilisateur ainsi que pour des paramètres invisibles par ce dernier. De plus, il présente une classe déléguée permettant de simplifier l’utilisation du fichier SharedPreferences de défaut depuis une Activity ou un Fragment.

Références

  1. Android Documentation: Settings
  2. Material Design: Settings
  3. Android Documentation: Save Key-Value Data with SharedPreferences
  4. Antonio Leiva: DelegatesExt
  5. DelegatesExt by macha on Gitlab
  6. Correction sur GitLab : MR 7 Shared Preferences

Partagez ou réagissez sur Twitter.

Vous avez trouvé une erreur ou voulez améliorer cet article ? Editez le directement !

Comments