Les adaptateurs de liaison sont chargés d'effectuer les appels de framework appropriés pour définir des valeurs. Par exemple, vous pouvez définir une valeur de propriété, ce qui peut être l'appel de la méthode setText()
. Un autre exemple consiste à définir un écouteur d'événements, comme l'appel de la méthode setOnClickListener()
.
La bibliothèque Data Binding vous permet de spécifier la méthode appelée pour définir une valeur, de fournir votre propre logique de liaison et de spécifier le type de l'objet renvoyé à l'aide d'adaptateurs.
Définir les valeurs des attributs
Chaque fois qu'une valeur liée change, la classe de liaison générée doit appeler une méthode "setter" sur la vue avec l'expression de liaison. Vous pouvez laisser la bibliothèque de liaison de données déterminer automatiquement la méthode, ou la déclarer explicitement ou fournir une logique personnalisée pour en sélectionner une.
Sélection automatique de la méthode
Pour un attribut nommé example
, la bibliothèque trouve automatiquement la méthode setExample(arg)
qui accepte les types compatibles comme argument. L'espace de noms de l'attribut n'est pas pris en compte. Seuls le nom et le type d'attribut sont utilisés lors de la recherche d'une méthode.
Par exemple, pour l'expression android:text="@{user.name}"
, la bibliothèque recherche une méthode setText(arg)
qui accepte le type renvoyé par user.getName()
. Si le type renvoyé de user.getName()
est String
, la bibliothèque recherche une méthode setText()
qui accepte un argument String
. Si l'expression renvoie un int
, la bibliothèque recherche une méthode setText()
qui accepte un argument int
. L'expression doit renvoyer le type correct. Vous pouvez convertir la valeur renvoyée si nécessaire.
La liaison de données fonctionne même s'il n'existe aucun attribut avec le nom donné. Vous pouvez créer des attributs pour n'importe quel setter à l'aide de la liaison de données. Par exemple, la classe de support DrawerLayout
n'a pas d'attributs, mais de nombreux setters. La mise en page suivante utilise automatiquement les méthodes setScrimColor(int)
et addDrawerListener(DrawerListener)
comme setter pour les attributs app:scrimColor
et app:drawerListener
, respectivement:
<androidx.drawerlayout.widget.DrawerLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:scrimColor="@{@color/scrim}"
app:drawerListener="@{fragment.drawerListener}">
Spécifier un nom de méthode personnalisé
Certains attributs ont des setters qui ne correspondent pas par leur nom. Dans ces situations, un attribut peut être associé au setter à l'aide de l'annotation BindingMethods
. L'annotation est utilisée avec une classe et peut contenir plusieurs annotations BindingMethod
, une pour chaque méthode renommée. Les méthodes de liaison sont des annotations que vous pouvez ajouter à n'importe quelle classe de votre application.
Dans l'exemple suivant, l'attribut android:tint
est associé à la méthode setImageTintList(ColorStateList)
, et non à la méthode setTint()
:
Kotlin
@BindingMethods(value = [ BindingMethod( type = android.widget.ImageView::class, attribute = "android:tint", method = "setImageTintList")])
Java
@BindingMethods({ @BindingMethod(type = "android.widget.ImageView", attribute = "android:tint", method = "setImageTintList"), })
En règle générale, il n'est pas nécessaire de renommer les setters dans les classes du framework Android. Les attributs sont déjà implémentés à l'aide de la convention de noms pour rechercher automatiquement les méthodes de correspondance.
Fournir une logique personnalisée
Certains attributs nécessitent une logique de liaison personnalisée. Par exemple, il n'existe pas de setter associé à l'attribut android:paddingLeft
. À la place, la méthode setPadding(left,
top, right, bottom)
est fournie. Une méthode d'adaptateur de liaison statique avec l'annotation BindingAdapter
vous permet de personnaliser la façon dont un setter est appelé pour un attribut.
Les attributs des classes du framework Android comportent déjà des annotations BindingAdapter
. L'exemple suivant montre l'adaptateur de liaison pour l'attribut paddingLeft
:
Kotlin
@BindingAdapter("android:paddingLeft") fun setPaddingLeft(view: View, padding: Int) { view.setPadding(padding, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom()) }
Java
@BindingAdapter("android:paddingLeft") public static void setPaddingLeft(View view, int padding) { view.setPadding(padding, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom()); }
Les types de paramètres sont importants. Le premier paramètre détermine le type de vue associé à l'attribut. Le deuxième paramètre détermine le type accepté dans l'expression de liaison pour l'attribut donné.
Les adaptateurs de liaison sont également utiles pour d'autres types de personnalisation. Par exemple, un chargeur personnalisé peut être appelé à partir d'un thread de nœud de calcul pour charger une image.
Vous pouvez également avoir des adaptateurs qui reçoivent plusieurs attributs, comme illustré dans l'exemple suivant:
Kotlin
@BindingAdapter("imageUrl", "error") fun loadImage(view: ImageView, url: String, error: Drawable) { Picasso.get().load(url).error(error).into(view) }
Java
@BindingAdapter({"imageUrl", "error"}) public static void loadImage(ImageView view, String url, Drawable error) { Picasso.get().load(url).error(error).into(view); }
Vous pouvez utiliser l'adaptateur dans votre mise en page, comme illustré dans l'exemple suivant. Notez que @drawable/venueError
fait référence à une ressource de votre application. Le fait d'entourer la ressource de @{}
en fait une expression de liaison valide.
<ImageView app:imageUrl="@{venue.imageUrl}" app:error="@{@drawable/venueError}" />
L'adaptateur est appelé si imageUrl
et error
sont utilisés pour un objet ImageView
, imageUrl
est une chaîne et error
est un Drawable
. Si vous souhaitez que l'adaptateur soit appelé lorsque l'un des attributs est défini, définissez l'indicateur facultatif requireAll
de l'adaptateur sur false
, comme illustré dans l'exemple suivant:
Kotlin
@BindingAdapter(value = ["imageUrl", "placeholder"], requireAll = false) fun setImageUrl(imageView: ImageView, url: String?, placeHolder: Drawable?) { if (url == null) { imageView.setImageDrawable(placeholder); } else { MyImageLoader.loadInto(imageView, url, placeholder); } }
Java
@BindingAdapter(value={"imageUrl", "placeholder"}, requireAll=false) public static void setImageUrl(ImageView imageView, String url, Drawable placeHolder) { if (url == null) { imageView.setImageDrawable(placeholder); } else { MyImageLoader.loadInto(imageView, url, placeholder); } }
Les méthodes d'adaptateur de liaison peuvent utiliser les anciennes valeurs dans leurs gestionnaires. Une méthode qui utilise des valeurs anciennes et nouvelles doit d'abord déclarer toutes les anciennes valeurs pour les attributs, puis les nouvelles, comme illustré dans l'exemple suivant:
Kotlin
@BindingAdapter("android:paddingLeft") fun setPaddingLeft(view: View, oldPadding: Int, newPadding: Int) { if (oldPadding != newPadding) { view.setPadding(newPadding, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom()) } }
Java
@BindingAdapter("android:paddingLeft") public static void setPaddingLeft(View view, int oldPadding, int newPadding) { if (oldPadding != newPadding) { view.setPadding(newPadding, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom()); } }
Les gestionnaires d'événements ne peuvent être utilisés qu'avec des interfaces ou des classes abstraites avec une seule méthode abstraite, comme illustré dans l'exemple suivant:
Kotlin
@BindingAdapter("android:onLayoutChange") fun setOnLayoutChangeListener( view: View, oldValue: View.OnLayoutChangeListener?, newValue: View.OnLayoutChangeListener? ) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { if (oldValue != null) { view.removeOnLayoutChangeListener(oldValue) } if (newValue != null) { view.addOnLayoutChangeListener(newValue) } } }
Java
@BindingAdapter("android:onLayoutChange") public static void setOnLayoutChangeListener(View view, View.OnLayoutChangeListener oldValue, View.OnLayoutChangeListener newValue) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { if (oldValue != null) { view.removeOnLayoutChangeListener(oldValue); } if (newValue != null) { view.addOnLayoutChangeListener(newValue); } } }
Utilisez ce gestionnaire d'événements dans votre mise en page comme suit:
<View android:onLayoutChange="@{() -> handler.layoutChanged()}"/>
Lorsqu'un écouteur comporte plusieurs méthodes, il doit être divisé en plusieurs écouteurs.
Par exemple, View.OnAttachStateChangeListener
comporte deux méthodes : onViewAttachedToWindow(View)
et onViewDetachedFromWindow(View)
.
La bibliothèque fournit deux interfaces pour différencier leurs attributs et leurs gestionnaires:
Kotlin
// Translation from provided interfaces in Java: @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) interface OnViewDetachedFromWindow { fun onViewDetachedFromWindow(v: View) } @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) interface OnViewAttachedToWindow { fun onViewAttachedToWindow(v: View) }
Java
@TargetApi(VERSION_CODES.HONEYCOMB_MR1) public interface OnViewDetachedFromWindow { void onViewDetachedFromWindow(View v); } @TargetApi(VERSION_CODES.HONEYCOMB_MR1) public interface OnViewAttachedToWindow { void onViewAttachedToWindow(View v); }
Étant donné que la modification d'un écouteur peut affecter l'autre, vous avez besoin d'un adaptateur qui fonctionne pour l'un ou l'autre des attributs. Vous pouvez définir requireAll
sur false
dans l'annotation pour spécifier qu'une expression de liaison ne doit pas être attribuée à tous les attributs, comme illustré dans l'exemple suivant:
Kotlin
@BindingAdapter( "android:onViewDetachedFromWindow", "android:onViewAttachedToWindow", requireAll = false ) fun setListener(view: View, detach: OnViewDetachedFromWindow?, attach: OnViewAttachedToWindow?) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) { val newListener: View.OnAttachStateChangeListener? newListener = if (detach == null && attach == null) { null } else { object : View.OnAttachStateChangeListener { override fun onViewAttachedToWindow(v: View) { attach.onViewAttachedToWindow(v) } override fun onViewDetachedFromWindow(v: View) { detach.onViewDetachedFromWindow(v) } } } val oldListener: View.OnAttachStateChangeListener? = ListenerUtil.trackListener(view, newListener, R.id.onAttachStateChangeListener) if (oldListener != null) { view.removeOnAttachStateChangeListener(oldListener) } if (newListener != null) { view.addOnAttachStateChangeListener(newListener) } } }
Java
@BindingAdapter({"android:onViewDetachedFromWindow", "android:onViewAttachedToWindow"}, requireAll=false) public static void setListener(View view, OnViewDetachedFromWindow detach, OnViewAttachedToWindow attach) { if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1) { OnAttachStateChangeListener newListener; if (detach == null && attach == null) { newListener = null; } else { newListener = new OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View v) { if (attach != null) { attach.onViewAttachedToWindow(v); } } @Override public void onViewDetachedFromWindow(View v) { if (detach != null) { detach.onViewDetachedFromWindow(v); } } }; } OnAttachStateChangeListener oldListener = ListenerUtil.trackListener(view, newListener, R.id.onAttachStateChangeListener); if (oldListener != null) { view.removeOnAttachStateChangeListener(oldListener); } if (newListener != null) { view.addOnAttachStateChangeListener(newListener); } } }
L'exemple ci-dessus est légèrement compliqué, car la classe View
utilise les méthodes addOnAttachStateChangeListener()
et removeOnAttachStateChangeListener()
au lieu d'une méthode setter pour OnAttachStateChangeListener
.
La classe android.databinding.adapters.ListenerUtil
permet d'effectuer le suivi de ces écouteurs afin qu'ils puissent être supprimés dans l'adaptateur de liaison.
Conversions d'objets
Conversion automatique d'objets
Lorsqu'un objet Object
est renvoyé à partir d'une expression de liaison, la bibliothèque sélectionne la méthode utilisée pour définir la valeur de la propriété. Object
est converti en un type de paramètre de la méthode choisie. Ce comportement est pratique dans les applications qui utilisent la classe ObservableMap
pour stocker des données, comme illustré dans l'exemple suivant:
<TextView
android:text='@{userMap["lastName"]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
L'objet userMap
de l'expression renvoie une valeur, qui est automatiquement convertie en type de paramètre trouvé dans la méthode setText(CharSequence)
utilisée pour définir la valeur de l'attribut android:text
. Si le type de paramètre est ambigu, convertissez le type renvoyé dans l'expression.
Conversions personnalisées
Dans certains cas, une conversion personnalisée est requise pour des types spécifiques. Par exemple, l'attribut android:background
d'une vue attend une valeur Drawable
, mais la valeur color
spécifiée est un entier. L'exemple suivant montre un attribut qui attend une valeur Drawable
, mais un entier est fourni à la place:
<View
android:background="@{isError ? @color/red : @color/white}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
Chaque fois qu'une valeur Drawable
est attendue et qu'un nombre entier est renvoyé, convertissez l'int
en ColorDrawable
.
Pour effectuer la conversion, utilisez une méthode statique avec une annotation BindingConversion
, comme suit:
Kotlin
@BindingConversion fun convertColorToDrawable(color: Int) = ColorDrawable(color)
Java
@BindingConversion public static ColorDrawable convertColorToDrawable(int color) { return new ColorDrawable(color); }
Toutefois, les types de valeurs fournis dans l'expression de liaison doivent être cohérents. Vous ne pouvez pas utiliser différents types dans la même expression, comme illustré dans l'exemple suivant:
// The @drawable and @color represent different value types in the same
// expression, which causes a build error.
<View
android:background="@{isError ? @drawable/error : @color/white}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
Ressources supplémentaires
Pour en savoir plus sur la liaison de données, consultez les ressources suivantes.
Exemples
Ateliers de programmation
Articles de blog
Recommandations personnalisées
- Remarque : Le texte du lien s'affiche lorsque JavaScript est désactivé
- Bibliothèque Data Binding
- Dispositions et expressions de liaison
- Liaison de vue