Использование привязки данных библиотеки, чтобы установить цвет фона ресурса или 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 ответов:
Причина:
Первое, что нужно знать, это то, что библиотека привязки данных уже предоставляет конвертер привязки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
:Передача 0 в качестве "нулевого идентификатора ресурса" безопасна, поскольку метод<data> <import type="com.example.package.R" /> <variable ... /> </data>
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
, он должен возвращайте aColorDrawable
, а не ADrawable
-в противном случае встроенный метод будет рассматриваться как более конкретный/подходящий.
Другой подход заключается в использовании статического метода для преобразования цвета в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