Использование привязки данных библиотеки, чтобы установить цвет фона ресурса или null


Я хотел бы установить цвет фона или null на моем представлении с помощью библиотеки привязки данных, но я получаю исключение, пытаясь запустить его.

java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.Integer.intValue()' on a null object reference

Вот как я это делаю:

android:background="@{article.sponsored ? @color/sponsored_article_background : null}"

Я также попытался настроить преобразование, но это не сработало.

@BindingConversion
public static ColorDrawable convertColorToDrawable(int color) {
   return new ColorDrawable(color);
}

В конце концов, я решил его с помощью обходного пути, используя @BindingAdapter, но я хотел бы знать, как это сделать правильно.

5 24

5 ответов:

Причина:

Первое, что нужно знать, это то, что библиотека привязки данных уже предоставляет конвертер привязки convertColorToDrawable, расположенный в android.databinding.adapters.Converters.convertColorToDrawable(int).

Использование android:background должно "теоретически" работать, потому что оно имеет соответствующее setBackground(Drawable) метод. Проблема в том, что он видит, что вы пытаетесь передать цвет в качестве первого аргумента, поэтому он попытался запустить этот конвертер, прежде чем применить его к методу setBackground(Drawable). Если databinding решит использовать конвертер, он будет использовать его для обоих аргументов, а также для null, непосредственно перед применением конечного результата к сеттеру.
Поскольку null не может быть кастами int (и вы не можете вызвать intValue() на него), он бросает NullPointerException.

Есть упоминание о том, что смешанные типы аргументов не поддерживаются в официальном руководстве по привязке данных.

Здесь два решения этой проблемы. Хотя вы можете использовать любое из этих двух решений, первое гораздо проще.

Решения:

1. Как drawable

Если вы определяете свой цвет не как цвет, а как рисуемый в ваших ресурсах (это может быть в наших цветах.xml-файл:
<drawable name="sponsored_article_background">#your_color</drawable>

Или

<drawable name="sponsored_article_background">@color/sponsored_article_background</drawable>

Тогда вы должны быть в состоянии использовать android:background, как вы первоначально хотели, но обеспечивая рисование вместо цвета:

android:background="@{article.sponsored ? @drawable/sponsored_article_background : null}"

Здесь аргументы имеют совместимые типы: первый - Drawable, а второй-null, поэтому он также может быть приведен к Drawable.

2. Как идентификатор ресурса

app:backgroundResource="@{article.sponsored ? R.color.sponsored_article_background : 0}"

Но это также будет требуется добавить импорт класса R в раздел data:

<data>
    <import type="com.example.package.R" />
    <variable ... />
</data>
Передача 0 в качестве "нулевого идентификатора ресурса" безопасна, поскольку метод setBackgroundResource View проверяет, отличается ли resid от 0, и устанавливает null в качестве фона, который можно нарисовать в противном случае. Там не создаются ненужные прозрачные рисованные объекты.
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;
}

Я думаю, что вы должны попробовать default color вместо null

Вот так

android:background="@{article.sponsored ? @color/sponsored_article_background : @color/your_default_color}"

Один из подходов, который вы можете использовать, - это написать пользовательский @BindingConversion, чтобы позаботиться об этом для вас:

@BindingConversion
public static ColorDrawable convertColorToDrawable(int color) {
    return color != 0 ? new ColorDrawable(color) : null;
}

С помощью этого вы можете установить любой атрибут, который принимает ColorDrawable целочисленное цветовое значение (например, 0 или @android:color/transparent) и автоматически преобразовать его в легковесный @null для вас.

(в то время как встроенный преобразователь convertColorToDrawable(int) всегда создает объект ColorDrawable, Даже если цвет прозрачен.)

Примечание: Для того, чтобы этот метод был использован вместо встроенного @BindingConversion, он должен возвращайте a ColorDrawable, а не A Drawable-в противном случае встроенный метод будет рассматриваться как более конкретный/подходящий.
Другой подход заключается в использовании статического метода для преобразования цвета в Drawable в выражении привязки данных, чтобы типы значений совпадали. Например, можно импортировать встроенный класс Converters:
<data>
    <import type="android.databinding.adapters.Converters"/>
</data>

...и напишите свое выражение так:

android:background="@{article.sponsored ? Converters.convertColorToDrawable(@color/sponsored_article_background) : null}"

...хотя лично я бы рекомендовал ставить этот вид условно логика в вашем методе адаптера привязки данных вместо этого, например, используя метод getArticleBackground(), который возвращает Drawable или null. В общем, все проще отлаживать и отслеживать, если вы избегаете помещать логику принятия решений в файлы макета.

Вы можете использовать @BindingAdapter ("android:background") и установить любые ресурсы.

Если вы пишете на Kotlin-просто скопируйте-вставьте в ваш проект: https://github.com/OlegTarashkevich/ObservableBackground

Попробуйте это:

@Bindable
private int color;

И в конструкторе

color = Color.parseColor("your color in String for examp.(#ffffff)")

В xml:

android:textColor = "@{data.color}"