web-dev-qa-db-fra.com

Utilisez la bibliothèque DataBinding pour définir la ressource de couleur d'arrière-plan ou null

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.

31
Damian Petla

Raison:

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.

Solutions:

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;
}
61
Maciej Ciemięga

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}"
9
pRaNaY

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.

7
Lorne Laliberte

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}"
1
C. Alen

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}" />
0
Amin Keshavarzian