Подтвердить что ты не робот

Используйте библиотеку 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, но я хотел бы знать, как это сделать правильно.

4b9b3361

Ответ 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}">