Je voudrais définir la couleur d'arrière-plan ou null
sur ma vue à l'aide de la bibliothèque DataBinding mais j'obtiens une exception en essayant de l'exécuter.
Java.lang.NullPointerException: Attempt to invoke virtual method 'int Java.lang.Integer.intValue()' on a null object reference
Voici comment je le fais:
Android:background="@{article.sponsored ? @color/sponsored_article_background : null}"
J'ai également essayé de définir la conversion, mais cela n'a pas fonctionné.
@BindingConversion
public static ColorDrawable convertColorToDrawable(int color) {
return new ColorDrawable(color);
}
Finalement, je l'ai résolu avec une solution de contournement en utilisant @BindingAdapter
mais j'aimerais savoir comment le faire correctement.
La première chose à savoir est que la bibliothèque DataBinding fournit déjà un convertisseur de liaison convertColorToDrawable
situé dans Android.databinding.adapters.Converters.convertColorToDrawable(int)
.
L'utilisation de Android:background
Devrait "théoriquement" fonctionner, car elle a une méthode setBackground(Drawable)
correspondante. Le problème est qu'il voit que vous essayez de passer une couleur comme premier argument, il a donc essayé de lancer ce convertisseur avant de l'appliquer à la méthode setBackground(Drawable)
. Si la liaison de données décide d'utiliser un convertisseur, elle l'utilisera sur les deux arguments, donc également sur null
, juste avant d'appliquer un résultat final à un setter.
Parce que null
ne peut pas être converti en int
(et vous ne pouvez pas y invoquer intValue()
), il lance NullPointerException
.
Il y a une mention sur le fait que les types d'arguments mixtes ne sont pas pris en charge dans --- Data Binding Guide .
Voici deux solutions à ce problème. Bien que vous puissiez utiliser l'une de ces deux solutions, la première est beaucoup plus simple.
1. As drawable
Si vous définissez votre couleur non pas comme une couleur mais comme un dessinable dans vos ressources (cela peut être dans notre fichier colors.xml:
<drawable name="sponsored_article_background">#your_color</drawable>
ou
<drawable name="sponsored_article_background">@color/sponsored_article_background</drawable>
alors vous devriez être en mesure d'utiliser Android:background
comme vous le vouliez à l'origine, mais en fournissant un dessin au lieu de la couleur:
Android:background="@{article.sponsored ? @drawable/sponsored_article_background : null}"
Ici, les arguments ont des types compatibles: le premier est Drawable
et le second est nul, il peut donc être converti en Drawable
.
2. Comme identifiant de ressource
app:backgroundResource="@{article.sponsored ? R.color.sponsored_article_background : 0}"
mais il faudra également ajouter votre importation de classe R dans la section data
:
<data>
<import type="com.example.package.R" />
<variable ... />
</data>
Passer 0 comme "id de ressource null" est sûr car la méthode setBackgroundResource
de View
vérifie si resid
est différente de 0 et définit null comme arrière-plan à dessiner autrement. Aucun objet dessinable transparent inutile n'est créé à cet endroit.
public void setBackgroundResource(int resid) {
if (resid != 0 && resid == mBackgroundResource) {
return;
}
Drawable d= null;
if (resid != 0) {
d = mResources.getDrawable(resid);
}
setBackgroundDrawable(d);
mBackgroundResource = resid;
}
Je pense que vous devez essayer par défaut color
au lieu de null
comme ça
Android:background="@{article.sponsored ? @color/sponsored_article_background : @color/your_default_color}"
Une approche que vous pouvez utiliser consiste à écrire un @BindingConversion
Personnalisé pour prendre soin de cela pour vous:
@BindingConversion
public static ColorDrawable convertColorToDrawable(int color) {
return color != 0 ? new ColorDrawable(color) : null;
}
Avec cela, vous pouvez définir n'importe quel attribut qui accepte un ColorDrawable
à une valeur de couleur entière (comme 0 ou @Android:color/transparent
) Et le convertir automatiquement en @null léger pour vous.
(Alors que le convertisseur convertColorToDrawable(int)
intégré crée toujours un objet ColorDrawable
, même si la couleur est transparente.)
Remarque: pour que cette méthode soit utilisée à la place du @BindingConversion
Intégré, elle doit renvoyer un ColorDrawable
et non un Drawable
- sinon le intégré sera considérée comme plus spécifique/appropriée.
Une autre approche consiste à utiliser une méthode statique pour convertir une couleur en Drawable
dans votre expression de liaison de données, afin de faire correspondre les types de valeur. Par exemple, vous pouvez importer la classe Converters
intégrée:
<data>
<import type="Android.databinding.adapters.Converters"/>
</data>
... et écrivez votre expression comme ceci:
Android:background="@{article.sponsored ? Converters.convertColorToDrawable(@color/sponsored_article_background) : null}"
... bien que je recommande personnellement de mettre ce type de logique conditionnelle dans votre méthode d'adaptateur de liaison de données à la place, par exemple en utilisant une méthode getArticleBackground()
qui retourne un Drawable ou null. En général, les choses sont plus faciles à déboguer et à suivre si vous évitez de placer la logique de décision dans vos fichiers de disposition.
Essaye ça:
@Bindable
private int color;
et dans le constructeur
color = Color.parseColor("your color in String for examp.(#ffffff)")
en xml:
Android:textColor = "@{data.color}"
Dans cet article vous pouvez trouver deux bonnes solutions, dans mon cas cependant, une seule de travail parce que je voulais changer la teinte de fond dans un bouton Material, voici mon adaptateur de liaison:
Créez d'abord un fichier Kotlin et collez cette méthode d'adaptateur:
package com.nyp.kartak.utilities
import Android.content.res.ColorStateList
import androidx.databinding.BindingAdapter
import com.google.Android.material.button.MaterialButton
import com.nyp.kartak.model.ReceiptUserPurchaseModel
@BindingAdapter("backgroundTintBinding")
fun backgroundTintBinding(button: MaterialButton, model: ReceiptUserPurchaseModel) {
button.backgroundTintList = ColorStateList.valueOf(button.resources.getColor( model.color))
}
Utilisez-le ensuite dans votre xml:
<data>
<variable
name="model"
type="com.nyp.kartak.model.ReceiptUserPurchaseModel" />
</data>
// .....
<com.google.Android.material.button.MaterialButton
Android:id="@+id/payBtn"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:text="@{model.getAction()}"
app:backgroundTintBinding="@{model}" />