Используйте библиотеку DataBinding для установки цвета фона или нулевого
Я хотел бы задать цвет фона или null
в моем представлении, используя библиотеку DataBinding, но я получаю исключение, пытаясь запустить его.
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
, но я хотел бы знать, как это сделать правильно.
Ответы
Ответ 1
Причина:
Прежде всего нужно знать, что библиотека DataBinding уже предоставляет конвертер привязки convertColorToDrawable
, расположенный в android.databinding.adapters.Converters.convertColorToDrawable(int)
.
Использование android:background
должно "теоретически" работать, потому что оно имеет соответствующий метод setBackground(Drawable)
. Проблема в том, что он видит, что вы пытаетесь передать цвет в качестве первого аргумента, поэтому он попытался запустить этот конвертер, прежде чем применять его к методу setBackground(Drawable)
. Если привязка данных решит использовать конвертер, он будет использовать его для обоих аргументов, а также на null
, прямо перед тем, как применить окончательный результат к установщику.
Поскольку null
не может быть кастами на int
(и вы не можете вызвать intValue()
на нем), он выдает NullPointerException
.
Существует упоминание о том, что смешанные типы аргументов не поддерживаются в официальном Руководстве по привязке данных.
Вот два решения этой проблемы. Хотя вы можете использовать любое из этих двух решений, первое намного проще.
Решение:
1. Как доступный
Если вы определяете свой цвет не как цвет, а как вытягиваемый в своих ресурсах (он может быть в нашем файле colors.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}"
Здесь аргументы имеют совместимые типы: first is 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;
}
Ответ 2
Я думаю, вам нужно попробовать по умолчанию color
вместо null
как это
android:background="@{article.sponsored ? @color/sponsored_article_background : @color/your_default_color}"
Ответ 3
Один из подходов, который вы можете использовать, - написать пользовательский @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
, он должен возвращать ColorDrawable
, а не 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. В целом, проще всего отлаживать и отслеживать, если вы не ставите логику принятия решения в свои файлы макета.
Ответ 4
Вы можете использовать @BindingAdapter ( "android: background" ) и установить любые ресурсы.
Если вы пишете в Kotlin - просто скопируйте-вставьте в проект:
https://github.com/OlegTarashkevich/ObservableBackground
Ответ 5
Попробуйте следующее:
@Bindable
private int color;
и в конструкторе
color = Color.parseColor("your color in String for examp.(#ffffff)")
в xml:
android:textColor = "@{data.color}"
Ответ 6
В этой статье вы можете найти два хороших решения, в моем случае, только одно из работ, потому что я хотел изменить оттенок фона в кнопке материала, здесь мой адаптер Binding:
Сначала создайте файл Kotlin и вставьте этот метод адаптера:
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))
}
Во-вторых, используйте его в своем 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}" />
Ответ 7
Вы можете использовать @BindingAdapter ("android: background") и установить любые ресурсы.
Если вы пишете в Kotlin - просто скопируйте и вставьте в ваш проект: https://github.com/OlegTarashkevich/ObservableBackground
Как это устроено:
class ViewModel {
val background = ObservableBackground()
fun setColor() {
background.setColorValue(Color.WHITE)
}
}
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@{viewModel.background}">