Principes de base d'Android en Kotlin 02.4 : Principes de base de la liaison de données

Cet atelier de programmation fait partie du cours Principes de base d'Android en Kotlin. Vous tirerez pleinement parti de ce cours en suivant les ateliers de programmation dans l'ordre. Tous les ateliers de programmation du cours sont listés sur la page de destination des ateliers de programmation Principes de base d'Android en Kotlin.

Introduction

Dans les ateliers de programmation précédents de ce cours, vous avez utilisé la fonction findViewById() pour obtenir des références aux vues. Lorsque votre application comporte des hiérarchies de vues complexes, findViewById() est coûteux et ralentit l'application, car Android parcourt la hiérarchie de vues, en commençant par la racine, jusqu'à ce qu'il trouve la vue souhaitée. Heureusement, il existe une meilleure solution.

Pour définir des données dans les vues, vous avez utilisé des ressources de chaîne et défini les données à partir de l'activité. Il serait plus efficace que la vue connaisse les données. Et heureusement, c'est possible.

Dans cet atelier de programmation, vous allez apprendre à utiliser la liaison de données pour éliminer le besoin de findViewById(). Vous apprendrez également à utiliser la liaison de données pour accéder aux données directement à partir d'une vue.

Ce que vous devez déjà savoir

Vous devez maîtriser les éléments suivants :

  • Qu'est-ce qu'une activité et comment en configurer une avec une mise en page dans onCreate() ?
  • Créer une vue de texte et définir le texte qu'elle affiche.
  • Utiliser findViewById() pour obtenir une référence à une vue.
  • Créer et modifier une mise en page XML de base pour une vue.

Points abordés

  • Comment utiliser la bibliothèque Data Binding pour éliminer les appels inefficaces vers findViewById().
  • Accéder aux données de l'application directement à partir du fichier XML.

Objectifs de l'atelier

  • Modifier une application pour qu'elle utilise le data binding au lieu de findViewById() et pour qu'elle accède aux données directement à partir des fichiers XML de mise en page.

Dans cet atelier de programmation, vous allez commencer par l'application AboutMe et la modifier pour qu'elle utilise la liaison de données. L'application sera exactement la même une fois que vous aurez terminé.

Voici ce que fait l'application AboutMe :

  • Lorsque l'utilisateur ouvre l'application, celle-ci affiche un nom, un champ permettant de saisir un pseudonyme, un bouton OK, une image d'étoile et du texte défilant.
  • L'utilisateur peut saisir un pseudo et appuyer sur le bouton OK. Le champ modifiable et le bouton sont remplacés par une vue de texte qui affiche le pseudo saisi.


Vous pouvez utiliser le code que vous avez créé dans l'atelier de programmation précédent ou télécharger le code AboutMeDataBinding-Starter depuis GitHub.

Le code que vous avez écrit dans les ateliers de programmation précédents utilise la fonction findViewById() pour obtenir des références aux vues.

Chaque fois que vous utilisez findViewById() pour rechercher une vue après sa création ou sa recréation, le système Android parcourt la hiérarchie des vues au moment de l'exécution pour la trouver. Lorsque votre application ne comporte que quelques vues, cela ne pose pas de problème. Toutefois, les applications de production peuvent comporter des dizaines de vues dans une mise en page, et même avec la meilleure conception, il y aura des vues imbriquées.

Imaginez une mise en page linéaire contenant une vue de défilement qui contient une vue de texte. Pour une hiérarchie de vues volumineuse ou profonde, la recherche d'une vue peut prendre suffisamment de temps pour ralentir sensiblement l'application pour l'utilisateur. La mise en cache des vues dans des variables peut être utile, mais vous devez toujours initialiser une variable pour chaque vue, dans chaque espace de noms. Avec de nombreuses vues et activités, cela s'accumule également.

Une solution consiste à créer un objet contenant une référence à chaque vue. Cet objet, appelé objet Binding, peut être utilisé par l'ensemble de votre application. Cette technique est appelée liaison de données. Une fois qu'un objet de liaison a été créé pour votre application, vous pouvez accéder aux vues et à d'autres données via l'objet de liaison, sans avoir à parcourir la hiérarchie des vues ni à rechercher les données.

L'association de données présente les avantages suivants :

  • Le code est plus court, plus facile à lire et plus facile à gérer que le code qui utilise findByView().
  • Les données et les vues sont clairement séparées. Cet avantage de la liaison de données deviendra de plus en plus important au cours de ce cours.
  • Le système Android ne parcourt la hiérarchie des vues qu'une seule fois pour obtenir chaque vue. Cela se produit au démarrage de l'application, et non au moment de l'exécution lorsque l'utilisateur interagit avec l'application.
  • Vous bénéficiez de la sécurité du typage pour accéder aux vues. (La sûreté de typage signifie que le compilateur valide les types lors de la compilation et génère une erreur si vous essayez d'attribuer le mauvais type à une variable.)

Dans cette tâche, vous allez configurer la liaison de données et l'utiliser pour remplacer les appels à findViewById() par des appels à l'objet de liaison.

Étape 1 : Activez la liaison de données

Pour utiliser la liaison de données, vous devez l'activer dans votre fichier Gradle, car elle n'est pas activée par défaut. En effet, la liaison de données augmente le temps de compilation et peut affecter le temps de démarrage de l'application.

  1. Si vous n'avez pas l'application AboutMe d'un atelier de programmation précédent, récupérez le code AboutMeDataBinding-Starter sur GitHub. Ouvrez-le dans Android Studio.
  2. Ouvrez le fichier build.gradle (Module: app).
  3. Dans la section android, avant l'accolade fermante, ajoutez une section dataBinding et définissez enabled sur true.
dataBinding {
    enabled = true
}
  1. Lorsque vous y êtes invité, synchronisez le projet. Si vous n'y êtes pas invité, sélectionnez File > Sync Project with Gradle Files (Fichier > Synchroniser le projet avec les fichiers Gradle).
  2. Vous pouvez exécuter l'application, mais vous ne verrez aucune modification.

Étape 2 : Modifiez le fichier de mise en page pour qu'il puisse être utilisé avec la liaison de données

Pour utiliser la liaison de données, vous devez encapsuler votre mise en page XML avec une balise <layout>. La classe racine n'est donc plus un groupe de vues, mais une mise en page contenant des groupes de vues et des vues. L'objet de liaison peut alors connaître la mise en page et les vues qu'elle contient.

  1. Ouvrez le fichier activity_main.xml.
  2. Accédez à l'onglet Texte.
  3. Ajoutez <layout></layout> en tant que balise extérieure autour de <LinearLayout>.
<layout>
   <LinearLayout ... >
   ...
   </LinearLayout>
</layout>
  1. Sélectionnez Code > Remettre en forme le code pour corriger l'indentation du code.

    Les déclarations d'espace de noms d'une mise en page doivent se trouver dans la balise la plus externe.
  1. Coupez les déclarations d'espace de noms de <LinearLayout> et collez-les dans la balise <layout>. Votre balise d'ouverture <layout> doit se présenter comme indiqué ci-dessous, et la balise <LinearLayout> ne doit contenir que des propriétés d'affichage.
<layout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto">
  1. Créez et exécutez votre application pour vérifier que vous avez effectué cette opération correctement.

Étape 3 : Créez un objet de liaison dans l'activité principale

Ajoutez une référence à l'objet de liaison à l'activité principale, afin de pouvoir l'utiliser pour accéder aux vues :

  1. Ouvrez le fichier MainActivity.kt.
  2. Avant onCreate(), au niveau supérieur, créez une variable pour l'objet de liaison. Cette variable est généralement appelée binding.

    Le type de binding, la classe ActivityMainBinding, est créé par le compilateur spécifiquement pour cette activité principale. Le nom est dérivé du nom du fichier de mise en page, à savoir activity_main + Binding.
private lateinit var binding: ActivityMainBinding
  1. Importez ActivityMainBinding, si Android Studio vous le demande. Si vous n'y êtes pas invité, cliquez sur ActivityMainBinding et appuyez sur Alt+Enter (Option+Enter sur Mac) pour importer cette classe manquante. (Pour en savoir plus sur les raccourcis clavier, consultez Raccourcis clavier.)

    L'instruction import doit ressembler à celle ci-dessous.
import com.example.android.aboutme.databinding.ActivityMainBinding

Ensuite, remplacez la fonction setContentView() actuelle par une instruction qui effectue les opérations suivantes :

  • Crée l'objet de liaison.
  • Utilise la fonction setContentView() de la classe DataBindingUtil pour associer la mise en page activity_main à MainActivity. Cette fonction setContentView() s'occupe également de la configuration de la liaison de données pour les vues.
  1. Dans onCreate(), remplacez l'appel setContentView() par la ligne de code suivante.
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
  1. Importez DataBindingUtil.
import androidx.databinding.DataBindingUtil

Étape 4 : Utilisez l'objet de liaison pour remplacer tous les appels à findViewById()

Vous pouvez maintenant remplacer tous les appels à findViewById() par des références aux vues qui se trouvent dans l'objet de liaison. Lorsque l'objet de liaison est créé, le compilateur génère les noms des vues dans l'objet de liaison à partir des ID des vues dans la mise en page, en les convertissant en casse mixte. Par exemple, done_button devient doneButton dans l'objet de liaison, nickname_edit devient nicknameEdit et nickname_text devient nicknameText.

  1. Dans onCreate(), remplacez le code qui utilise findViewById() pour trouver le done_button par le code qui fait référence au bouton dans l'objet de liaison.

    Remplacez ce code : findViewById<Button>(R.id.done_button)
    par : binding.doneButton

    Votre code final pour définir l'écouteur de clics dans onCreate() devrait ressembler à ceci.
binding.doneButton.setOnClickListener {
   addNickname(it)
}
  1. Faites de même pour tous les appels à findViewById() dans la fonction addNickname().
     Remplacez toutes les occurrences de findViewById<View>(R.id.id_view) par binding.idView. Pour ce faire :
  • Supprimez les définitions des variables editText et nicknameTextView ainsi que leurs appels à findViewById(). Cela générera des erreurs.
  • Corrigez les erreurs en obtenant les vues nicknameText, nicknameEdit et doneButton à partir de l'objet binding au lieu des variables (supprimées).
  • Remplacez view.visibility par binding.doneButton.visibility. L'utilisation de binding.doneButton au lieu de view transmis rend le code plus cohérent.

    Le résultat est le code suivant :
binding.nicknameText.text = binding.nicknameEdit.text
binding.nicknameEdit.visibility = View.GONE
binding.doneButton.visibility = View.GONE
binding.nicknameText.visibility = View.VISIBLE
  • Le fonctionnement reste inchangé. Vous pouvez éventuellement supprimer le paramètre view et mettre à jour toutes les utilisations de view pour utiliser binding.doneButton dans cette fonction.
  1. nicknameText nécessite un String, et nicknameEdit.text est un Editable. Lorsque vous utilisez la liaison de données, il est nécessaire de convertir explicitement le Editable en String.
binding.nicknameText.text = binding.nicknameEdit.text.toString()
  1. Vous pouvez supprimer les importations grisées.
  2. Kotlinisez la fonction à l'aide de apply{}.
binding.apply {
   nicknameText.text = nicknameEdit.text.toString()
   nicknameEdit.visibility = View.GONE
   doneButton.visibility = View.GONE
   nicknameText.visibility = View.VISIBLE
}
  1. Compilez et exécutez votre application. Elle devrait avoir le même aspect et fonctionner exactement comme avant.

Vous pouvez tirer parti de la liaison de données pour rendre une classe de données directement disponible dans une vue. Cette technique simplifie le code et est extrêmement utile pour gérer les cas plus complexes.

Dans cet exemple, au lieu de définir le nom et le surnom à l'aide de ressources de chaîne, vous allez créer une classe de données pour le nom et le surnom. Vous mettez la classe de données à la disposition de la vue à l'aide de la liaison de données.

Étape 1 : Créer la classe de données MyName

  1. Dans Android Studio, dans le répertoire java, ouvrez le fichier MyName.kt. Si vous ne disposez pas de ce fichier, créez-en un et appelez-le MyName.kt.
  2. Définissez une classe de données pour le nom et le surnom. Utilisez des chaînes vides comme valeurs par défaut.
data class MyName(var name: String = "", var nickname: String = "")

Étape 2 : Ajoutez des données à la mise en page

Dans le fichier activity_main.xml, le nom est actuellement défini dans un TextView à partir d'une ressource de chaîne. Vous devez remplacer la référence au nom par une référence aux données de la classe de données.

  1. Ouvrez activity_main.xml dans l'onglet Texte.
  2. En haut de la mise en page, entre les balises <layout> et <LinearLayout>, insérez une balise <data></data>. C'est ici que vous associerez la vue aux données.
<data>
  
</data>

Dans les balises de données, vous pouvez déclarer des variables nommées qui contiennent une référence à une classe.

  1. Dans la balise <data>, ajoutez une balise <variable>.
  2. Ajoutez un paramètre name pour attribuer à la variable le nom "myName". Ajoutez un paramètre type et définissez le type sur le nom complet de la classe de données MyName (nom du package + nom de la variable).
<variable
       name="myName"
       type="com.example.android.aboutme.MyName" />

Désormais, au lieu d'utiliser la ressource de chaîne pour le nom, vous pouvez référencer la variable myName.

  1. Remplacez android:text="@string/name" par le code ci-dessous.

@={} est une directive permettant d'obtenir les données référencées entre accolades.

myName fait référence à la variable myName que vous avez définie précédemment, qui pointe vers la classe de données myName et récupère la propriété name de la classe.

android:text="@={myName.name}"

Étape 3 : Créez les données

Vous disposez maintenant d'une référence aux données dans votre fichier de mise en page. Vous allez maintenant créer les données.

  1. Ouvrez le fichier MainActivity.kt.
  2. Au-dessus de onCreate(), créez une variable privée, également appelée myName par convention. Attribuez à la variable une instance de la classe de données MyName en transmettant le nom.
private val myName: MyName = MyName("Aleks Haecky")
  1. Dans onCreate(), définissez la valeur de la variable myName dans le fichier de mise en page sur la valeur de la variable myName que vous venez de déclarer. Vous ne pouvez pas accéder directement à la variable dans le code XML. Vous devez y accéder via l'objet de liaison.
binding.myName = myName
  1. Une erreur peut s'afficher, car vous devez actualiser l'objet de liaison après avoir apporté des modifications. Compilez votre application. L'erreur devrait disparaître.

Étape 4 : Utilisez la classe de données pour le pseudo dans la TextView

La dernière étape consiste à utiliser également la classe de données pour le pseudo dans TextView.

  1. Ouvrez activity_main.xml.
  2. Dans la vue de texte nickname_text, ajoutez une propriété text. Référencez nickname dans la classe de données, comme indiqué ci-dessous.
android:text="@={myName.nickname}"
  1. Dans ActivityMain, remplacez
    nicknameText.text = nicknameEdit.text.toString()
    par le code permettant de définir le pseudo dans la variable myName.
myName?.nickname = nicknameEdit.text.toString()

Une fois le pseudo défini, vous souhaitez que votre code actualise l'UI avec les nouvelles données. Pour ce faire, vous devez invalider toutes les expressions de liaison afin qu'elles soient recréées avec les données correctes.

  1. Ajoutez invalidateAll() après avoir défini le pseudo afin que l'UI soit actualisée avec la valeur de l'objet de liaison mis à jour.
binding.apply {
   myName?.nickname = nicknameEdit.text.toString()
   invalidateAll()
   ...
}
  1. Compilez et exécutez votre application. Elle devrait fonctionner exactement comme avant.

Projet Android Studio : AboutMeDataBinding

Étapes à suivre pour utiliser la liaison de données afin de remplacer les appels à findViewById() :

  1. Activez la liaison de données dans la section Android du fichier build.gradle :
    dataBinding { enabled = true }
  2. Utilisez <layout> comme vue racine dans votre mise en page XML.
  3. Définissez une variable de liaison :
    private lateinit var binding: ActivityMainBinding
  4. Créez un objet de liaison dans MainActivity, en remplaçant setContentView :
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
  5. Remplacez les appels à findViewById() par des références à la vue dans l'objet de liaison. Par exemple :
    findViewById<Button>(R.id.done_button) ⇒ binding.doneButton
    (dans l'exemple, le nom de la vue est généré en casse mixte à partir de id de la vue dans le fichier XML).

Voici comment lier des vues à des données :

  1. Créez une classe de données pour vos données.
  2. Ajoutez un bloc <data> dans la balise <layout>.
  3. Définissez un <variable> avec un nom et un type correspondant à la classe de données.
<data>
   <variable
       name="myName"
       type="com.example.android.aboutme.MyName" />
</data>
  1. Dans MainActivity, créez une variable avec une instance de la classe de données. Exemple :
    private val myName: MyName = MyName("Aleks Haecky")
  1. Dans l'objet de liaison, définissez la variable sur celle que vous venez de créer :
    binding.myName = myName
  1. Dans le code XML, définissez le contenu de la vue sur la variable que vous avez définie dans le bloc <data>. Utilisez la notation par points pour accéder aux données à l'intérieur de la classe de données.
    android:text="@={myName.name}"

Cours Udacity :

Documentation pour les développeurs Android :

Cette section répertorie les devoirs possibles pour les élèves qui suivent cet atelier de programmation dans le cadre d'un cours animé par un enseignant. Il revient à l'enseignant d'effectuer les opérations suivantes :

  • Attribuer des devoirs si nécessaire
  • Indiquer aux élèves comment rendre leurs devoirs
  • Noter les devoirs

Les enseignants peuvent utiliser ces suggestions autant qu'ils le souhaitent, et ne doivent pas hésiter à attribuer d'autres devoirs aux élèves s'ils le jugent nécessaire.

Si vous suivez cet atelier de programmation par vous-même, n'hésitez pas à utiliser ces devoirs pour tester vos connaissances.

Répondre aux questions suivantes

Question 1

Pourquoi limiter les appels explicites et implicites à findViewById() ?

  • Chaque fois que findViewById() est appelé, il traverse la hiérarchie des vues.
  • findViewById() s'exécute sur le thread principal ou le thread de l'UI.
  • Ces appels peuvent ralentir l'interface utilisateur.
  • Votre application est moins susceptible de planter.

Question 2

Comment décririez-vous la liaison de données ?

Par exemple, voici quelques exemples de ce que vous pourriez dire sur la liaison de données :

  • L'idée principale de la liaison de données est de créer un objet qui connecte/mappe/lie deux informations distantes au moment de la compilation, afin que vous n'ayez pas à rechercher les données au moment de l'exécution.
  • L'objet qui vous présente ces liaisons est appelé objet de liaison.
  • L'objet de liaison est créé par le compilateur.

Question 3

Parmi les propositions suivantes, laquelle n'est PAS un avantage de la liaison de données ?

  • Le code est plus court, et donc plus facile à lire et à gérer.
  • Les données et les vues sont clairement séparées.
  • Le système Android ne traverse la hiérarchie des vues qu'une seule fois pour obtenir chaque vue.
  • L'appel de findViewById() génère une erreur de compilation.
  • Sûreté du typage pour accéder aux vues.

Question 4

Quelle est la fonction de la balise <layout> ?

  • Vous l'enveloppez autour de votre vue racine dans la mise en page.
  • Des liaisons sont créées pour toutes les vues d'une mise en page.
  • Il désigne la vue de premier niveau dans une mise en page XML qui utilise la liaison de données.
  • Vous pouvez utiliser la balise <data> à l'intérieur d'une balise <layout> pour associer une variable à une classe de données.

Question 5

Quelle est la bonne méthode pour référencer des données liées dans la mise en page XML ?

  • android:text="@={myDataClass.property}"
  • android:text="@={myDataClass}"
  • android:text="@={myDataClass.property.toString()}"
  • android:text="@={myDataClass.bound_data.property}"

Passez à la leçon suivante : 3.1 : Créer un fragment

Pour obtenir des liens vers d'autres ateliers de programmation de ce cours, consultez la page de destination des ateliers de programmation Principes de base d'Android en Kotlin.