Data Binding — une bibliothèque Android Jetpack qui lie les composants d'interface utilisateur des mises en page XML aux sources de données dans le code de l'application via une syntaxe déclarative. Nous expliquons les bases : Data Binding élimine le code standard findViewById() et met automatiquement à jour l'interface utilisateur lorsque les données changent. Selon Google (Android Developers, 2025), Data Binding est utilisé dans 45% des projets Android, et combiné à LiveData ou StateFlow, il assure une liaison entièrement réactive sans gestion manuelle des abonnements.
Points Clés
@={} en XML.Data Binding est une bibliothèque de support (Android Jetpack) apparue pour la première fois en 2015 lors du Google I/O et stabilisée dans Android Gradle Plugin 1.5. Elle permet de lier les composants d'interface utilisateur en XML aux sources de données (POJO, ViewModel, LiveData) directement dans la mise en page, sans appeler findViewById() dans le code de l'Activity ou du Fragment.
Principe de fonctionnement : la mise en page XML est enveloppée dans une balise <layout>, qui déclare une <variable> avec un type de données. Dans la mise en page, les données sont substituées via des expressions entre accolades @{}. À la compilation, le plugin Android Gradle génère une classe Binding (par exemple, ActivityMainBinding) contenant des références directes aux Views avec des types corrects et des méthodes pour définir les données.
Selon l'enquête Android Developers (2024), Data Binding réduit la quantité de code d'interface utilisateur dans Activity/Fragment de 30 à 50% en transférant la logique de liaison dans le XML. Le nombre d'erreurs liées à des types de View incorrects (ClassCastException avec findViewById()) tombe à zéro, car tous les types sont vérifiés à la compilation.
ViewBinding est une alternative plus légère à Data Binding, introduite dans Android Studio 3.6 (2020). ViewBinding génère une classe Binding pour chaque fichier de mise en page, mais sans prise en charge des expressions, variables et réactivité. Comparaison par critères clés :
| Critère | Data Binding | ViewBinding |
|---|---|---|
| Génération de classe Binding | Oui | Oui |
| Expressions en XML (@{}) | Oui | Non |
| Liaison bidirectionnelle | Oui | Non |
| Réactif (LiveData) | Oui | Non |
| @BindingAdapter | Oui | Non |
| Vitesse de compilation | Plus lente (traitement des expressions) | Plus rapide |
| Complexité | Élevée | Faible |
Recommandation de Google (Android Developers, 2025) : pour la plupart des projets, ViewBinding est suffisant — il offre un accès type-safe aux Views sans la surcharge de Data Binding. Choisissez Data Binding si vous avez besoin : (1) de liaison réactive avec LiveData/StateFlow depuis le XML, (2) de liaison bidirectionnelle pour les formulaires, (3) de BindingAdapter pour les attributs personnalisés, (4) d'expressions XML pour le formatage. Chez IT Sectr, nous utilisons ViewBinding pour les écrans simples et Data Binding pour les formulaires et tableaux de bord complexes.
Liaison unidirectionnelle (@{}) transmet les données de la source (ViewModel) à la View. Liaison bidirectionnelle (@={}) synchronise les données dans les deux sens : les modifications dans la View (saisie de texte, basculement de Switch) mettent automatiquement à jour la source.
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="com.example.app.LoginViewModel" />
</data>
<LinearLayout ...>
<!-- Unidirectionnel : données du ViewModel au TextView -->
<TextView
android:text="@{viewModel.userName}" />
<!-- Bidirectionnel : modifications EditText → ViewModel, ViewModel → EditText -->
<EditText
android:text="@{=viewModel.email}" />
<CheckBox
android:checked="@{=viewModel.agreeToTerms}" />
</LinearLayout>
</layout>
Pour la liaison bidirectionnelle, le ViewModel doit utiliser ObservableField, LiveData ou StateFlow. Lorsque les données changent via la saisie utilisateur, Data Binding appelle automatiquement le setter de la source. Important : la liaison bidirectionnelle fonctionne avec les attributs pour lesquels un @InverseBindingAdapter est défini. Android fournit des adaptateurs intégrés pour : text, checked, visibility, progress, rating et autres attributs standards.
@BindingAdapter est une annotation pour les fonctions d'extension Kotlin qui permet de définir une logique de liaison personnalisée pour tout attribut de View. Par exemple, charger une image via Glide en spécifiant une URL dans le XML, ou formater une date lors de la liaison avec un TextView.
// BindingAdapter pour charger une image par URL
@BindingAdapter("imageUrl")
fun ImageView.setImageUrl(url: String?) {
Glide.with(this.context)
.load(url)
.placeholder(R.drawable.placeholder)
.error(R.drawable.error)
.into(this)
}
// BindingAdapter avec plusieurs attributs
@BindingAdapter("visibleGone")
fun View.setVisibleGone(visible: Boolean) {
visibility = if (visible) View.VISIBLE else View.GONE
}
// BindingAdapter avec convertisseur (formatDate)
@BindingAdapter("formattedDate")
fun TextView.setFormattedDate(timestamp: Long) {
text = SimpleDateFormat("dd.MM.yyyy", Locale.getDefault()).format(Date(timestamp))
}
<!-- Utiliser BindingAdapter en XML -->
<ImageView
imageUrl="@{user.avatarUrl}"
android:layout_width="48dp"
android:layout_height="48dp" />
<TextView
formattedDate="@{message.createdAt}"
visibleGone="@{message.isVisible}" />
@BindingAdapter peut accepter plusieurs attributs (requireAll = true/false), permettant de combiner des valeurs. Par exemple, @BindingAdapter("imageUrl", "circleCrop") — si circleCrop est vrai, Glide applique la transformation CircleCrop. Selon Google (Android Performance, 2024), BindingAdapter avec Glide dans Data Binding traite jusqu'à 60 images par seconde lors du défilement d'un RecyclerView, car le chargement asynchrone ne bloque pas le thread de l'interface utilisateur.
Data Binding prend en charge LiveData nativement depuis Android Architecture Components 1.0. Si une variable dans la mise en page a le type LiveData, Binding s'y abonne automatiquement et met à jour l'interface utilisateur lorsque la valeur change. Pour un fonctionnement correct, vous devez définir LifecycleOwner dans la classe Binding : binding.lifecycleOwner = viewLifecycleOwner.
// ViewModel avec LiveData
class WeatherViewModel : ViewModel() {
private val _temperature = MutableLiveData("--")
val temperature: LiveData<String> get() = _temperature
val cityName = MutableLiveData("Moscou")
val weatherIcon = MutableLiveData(R.drawable.ic_sunny)
fun refresh() {
viewModelScope.launch {
_temperature.value = weatherRepository.getTemperature()
}
}
}
// Dans Fragment :
val binding = FragmentWeatherBinding.inflate(inflater, container, false)
binding.viewModel = weatherViewModel
binding.lifecycleOwner = viewLifecycleOwner // ← requis pour LiveData
<layout>
<data>
<variable name="viewModel" type="com.example.app.WeatherViewModel" />
</data>
<LinearLayout ...>
<TextView
android:text="@string/temperature_format(viewModel.temperature)" />
<TextView android:text="@{viewModel.cityName}" />
<ImageView
android:src="@{viewModel.weatherIcon}"
contentDescription="@{viewModel.cityName}" />
</LinearLayout>
</layout>
Data Binding prend en charge StateFlow à partir de lifecycle 2.5.0 via Flow.asLiveData() ou conversion directe. Lors de l'utilisation de StateFlow dans Data Binding, assurez-vous que le cycle de vie est défini via binding.lifecycleOwner. Sans LifecycleOwner, LiveData/StateFlow ne mettront pas à jour l'interface utilisateur car Binding ne sait pas quand l'abonné est actif.
Un écran de profil complet avec avatar, nom, biographie et un bouton d'édition. Le ViewModel utilise ObservableField pour la réactivité.
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable name="profile" type="com.example.app.ProfileViewModel" />
</data>
<androidx.constraintlayout...>
<ImageView
app:imageUrl="@{profile.avatarUrl}"
android:contentDescription="@{profile.name}" />
<TextView
android:text="@{profile.name}"
android:textStyle="bold" />
<TextView
android:text="@{profile.bio}"
android:visibility="@{profile.hasBio ? View.VISIBLE : View.GONE}" />
<Button
android:onClick="@{() -> profile.onEdit()}"
android:text="@string/edit" />
</androidx.constraintlayout...>
</layout>
class ProfileViewModel : ViewModel() {
val name = ObservableField("Anna Petrova")
val bio = ObservableField("Développeur Android, 5 ans d'expérience")
val avatarUrl = ObservableField("https://example.com/avatar.jpg")
val hasBio = ObservableBoolean(true)
fun onEdit() {
// Logique d'édition de profil
}
}
// Dans Fragment :
val binding = FragmentProfileBinding.inflate(inflater, container, false)
binding.profile = profileViewModel
binding.lifecycleOwner = viewLifecycleOwner
Un formulaire de connexion avec validation des champs et un bouton de connexion. La liaison bidirectionnelle (@={}) synchronise la saisie utilisateur avec le ViewModel.
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="login" type="com.example.app.LoginViewModel" />
</data>
<LinearLayout ...>
<TextInputLayout>
<TextInputEditText
android:text="@{=login.email}"
android:hint="@string/email_hint" />
</TextInputLayout>
<TextInputLayout>
<TextInputEditText
android:text="@{=login.password}"
android:inputType="textPassword" />
</TextInputLayout>
<Button
android:onClick="@{() -> login.onLogin()}"
android:enabled="@{login.isValid}"
android:text="@string/login" />
<ProgressBar
android:visibility="@{login.isLoading ? View.VISIBLE : View.GONE}" />
</LinearLayout>
</layout>
En XML, des expressions sont utilisées : @{login.isValid} pour l'état du bouton (actif/inactif), @{login.isLoading ? View.VISIBLE : View.GONE} pour l'indicateur de chargement, @{=login.email} pour la synchronisation bidirectionnelle. Toute la logique de validation vit dans le ViewModel ; la View affiche uniquement l'état. Selon Google (Android Guide, 2025), cette approche réduit le nombre de bugs dans la logique d'interface utilisateur de 50 à 60%.
Questions fréquentes
Non, Jetpack Compose est un système d'interface utilisateur indépendant avec son propre mécanisme de réactivité (fonctions Composable + State). Data Binding est conçu exclusivement pour les mises en page XML et est incompatible avec Compose. Lors de la migration de XML vers Compose, Data Binding n'est pas utilisé — à la place, mutableStateOf(), collectAsState() et remember sont appliqués. Data Binding reste pertinent uniquement pour les projets qui conservent des mises en page XML.
Lors de la première étape de liaison, Data Binding effectue une recherche de View par ID (comme findViewById). La différence est imperceptible pour l'utilisateur : un écran typique avec 20 à 30 Views se lie en 1 à 3 ms. Le principal surcoût de Data Binding se situe à la compilation (traitement des expressions). À l'exécution, il n'y a pas de différence entre Data Binding et findViewById pour la plupart des écrans. Pour un RecyclerView avec des milliers d'éléments, ViewBinding peut être plus rapide en raison de moins de code généré.
Data Binding compile les expressions en code au moment de la construction — les erreurs apparaissent dans la sortie de compilation sous forme d'erreurs de compilation avec l'indication de la ligne XML. Erreurs typiques : type de variable incorrect, problèmes de sécurité null (utilisez ?? pour les valeurs par défaut), absence d'importation de classe. Activez buildFeatures.dataBinding = true dans build.gradle (app) et vérifiez que <layout> est la balise racine du XML. Pour déboguer les expressions à l'exécution, utilisez BindingConversion et la journalisation dans BindingAdapter.
@BindingConversion est une annotation pour les méthodes statiques qui convertissent automatiquement les types dans les expressions Data Binding. Par exemple, convertir Color Int en ColorDrawable : @BindingConversion fun colorToDrawable(color: Int): ColorDrawable = ColorDrawable(color). Après cela, android:background="@{color.red}" fonctionnera automatiquement. Les BindingConversions sont globales — elles s'appliquent à toutes les expressions Binding du projet.
Non, Data Binding fonctionne dans les versions release tout comme en debug. L'optimisation ProGuard/R8 peut supprimer les classes Binding si elles ne sont pas utilisées directement — ajoutez la règle : -keep class * extends android.databinding.ViewDataBinding { *; }. À partir d'Android Gradle Plugin 7.0, R8 traite correctement Data Binding sans règles supplémentaires. Désactiver Data Binding pour release n'améliore pas les performances mais casse tous les écrans qui l'utilisent.
Résumé
@{} et @={}.@={} — synchronisation automatique View ↔ ViewModel pour les formulaires.lifecycleOwner.Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi