使用DataBinding库设置背景颜色资源或null
Use DataBinding library to set background color resource or null
我想使用 DataBinding 库在我的视图中设置背景颜色或 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
解决了这个问题,但我想知道如何正确地做到这一点。
我认为你必须尝试默认 color
而不是 null
像这样
android:background="@{article.sponsored ? @color/sponsored_article_background : @color/your_default_color}"
原因:
首先要知道的是,DataBinding 库已经提供了一个 convertColorToDrawable
绑定转换器,位于 android.databinding.adapters.Converters.convertColorToDrawable(int)
.
使用 android:background
应该 "theoretically" 有效,因为它有相应的 setBackground(Drawable)
方法。问题在于它看到您尝试将颜色作为第一个参数传递,因此它尝试在将其应用于 setBackground(Drawable)
方法之前启动此转换器。如果数据绑定决定使用转换器,它将在两个参数上使用它,所以也在 null
上,就在将最终结果应用于 setter.
之前
因为 null
不能成为 int
的种姓(并且你不能在其上调用 intValue()
)它抛出 NullPointerException
.
官方提到不支持混合参数类型Data Binding Guide。
这里有两个解决这个问题的方法。虽然您可以使用这两种解决方案中的任何一种,但第一种要容易得多。
解决方案:
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}"
这里的参数有兼容的类型:第一个是 Drawable
第二个是 null 所以它也可以转换为 Drawable
.
2。作为资源 id
app:backgroundResource="@{article.sponsored ? R.color.sponsored_article_background : 0}"
但它还需要在 data
部分添加 R class 导入:
<data>
<import type="com.example.package.R" />
<variable ... />
</data>
将 0 作为 "null resource id" 传递是安全的,因为 View
的 setBackgroundResource
方法会检查 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;
}
您可以使用的一种方法是编写自定义 @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
,它必须 return 一个 ColorDrawable
而不是 Drawable
- - 否则内置方法将被视为更多 specific/appropriate.
另一种方法是在数据绑定表达式中使用静态方法将颜色转换为 Drawable
,以使值类型匹配。例如,您可以导入内置 Converters
class:
<data>
<import type="androidx.databinding.adapters.Converters"/>
</data>
...然后这样写你的表达式:
android:background="@{article.sponsored ? Converters.convertColorToDrawable(@color/sponsored_article_background) : null}"
...虽然我个人建议将这种条件逻辑放在数据绑定适配器方法中,例如使用 getArticleBackground()
方法 return 是 Drawable 或 null。一般来说,如果您避免在布局文件中放置决策逻辑,事情会更容易调试和跟踪。
试试这个:
@Bindable
private int color;
并在构造函数中
color = Color.parseColor("your color in String for examp.(#ffffff)")
在xml中:
android:textColor = "@{data.color}"
在这个 article 中,您可以找到两个很好的解决方案,但就我而言,只有一个可行,因为我想更改 Material 按钮中的背景色调,这是我的绑定适配器:
首先,创建一个 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}" />
它真的很旧 post 但我想再推荐一个解决方案。
- 在绘图中声明自定义 STYLES/BACKGROUNDS**
我有四种相似的样式,所以我只粘贴其中一种。
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/colorAccent">
<item>
<layer-list>
<item>
<shape android:shape="oval">
<stroke
android:width="1dp"
android:color="@color/colorAccent" />
<solid android:color="@color/colorPrimaryDark" />
<size
android:width="200dp"
android:height="200dp" />
</shape>
</item>
<item
android:width="100dp"
android:height="100dp"
android:gravity="center"
android:drawable="@drawable/ic_car_white" />
</layer-list>
</item>
</ripple>
当我设置此样式时,我的按钮如下所示:
- 在 model/handler class
中准备可绑定值
在我的例子中,我在 ActivityMainEventHandler class
中有以下代码
@Bindable
public Drawable getConenctButtonStyle() {
// here i'm checking connection state but you can do own conditions
ConnectionState state = Communication.getInstance().getConnectionState();
if (state != null) switch (state) {
case CONNECTED:
return ctx.getDrawable(R.drawable.circle_btn_state_green);
case CONNECTING:
case DISCONNECTING:
return ctx.getDrawable(R.drawable.circle_btn_state_orange);
case DISCONNECTED:
return ctx.getDrawable(R.drawable.circle_btn_state_red);
}
return ctx.getDrawable(R.drawable.circle_btn_state_first);
}
- 将您的 class 传递给您的视图
Activity onCreate:
bind = DataBindingUtil.setContentView(this, R.layout.activity_main);
handler = new ActivityMainEventHandler(this, bind);
bind.setMainHandler(handler);
我们的 XML activity
<data>
<variable
name="mainHandler"
type="xx.xxx.packagename.eventHandlers.ActivityMainEventHandler" />
</data>
- 将您的背景设置为如下所示
标记:
android:background="@{mainHandler.conenctButtonStyle}"
- 然后如果你想再次检查步骤 2 的条件并重绘视图,只需调用
代码:
//BR.conenctButtonStyle it's automatically generated id
bind.notifyPropertyChanged(BR.conenctButtonStyle);
另一个解决方案,如果你只想设置 backgroundTint
,而不是整个 background
,你可以这样使用它:
如果您的最小 api 是 21,您将需要导入 ContextCompat
:
<import type="androidx.core.content.ContextCompat" />
...
app:backgroundTintList="@{ContextCompat.getColorStateList(context, [funtion_to_get_your_color_res_id]))}"
我想使用 DataBinding 库在我的视图中设置背景颜色或 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
解决了这个问题,但我想知道如何正确地做到这一点。
我认为你必须尝试默认 color
而不是 null
像这样
android:background="@{article.sponsored ? @color/sponsored_article_background : @color/your_default_color}"
原因:
首先要知道的是,DataBinding 库已经提供了一个 convertColorToDrawable
绑定转换器,位于 android.databinding.adapters.Converters.convertColorToDrawable(int)
.
使用 android:background
应该 "theoretically" 有效,因为它有相应的 setBackground(Drawable)
方法。问题在于它看到您尝试将颜色作为第一个参数传递,因此它尝试在将其应用于 setBackground(Drawable)
方法之前启动此转换器。如果数据绑定决定使用转换器,它将在两个参数上使用它,所以也在 null
上,就在将最终结果应用于 setter.
之前
因为 null
不能成为 int
的种姓(并且你不能在其上调用 intValue()
)它抛出 NullPointerException
.
官方提到不支持混合参数类型Data Binding Guide。
这里有两个解决这个问题的方法。虽然您可以使用这两种解决方案中的任何一种,但第一种要容易得多。
解决方案:
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}"
这里的参数有兼容的类型:第一个是 Drawable
第二个是 null 所以它也可以转换为 Drawable
.
2。作为资源 id
app:backgroundResource="@{article.sponsored ? R.color.sponsored_article_background : 0}"
但它还需要在 data
部分添加 R class 导入:
<data>
<import type="com.example.package.R" />
<variable ... />
</data>
将 0 作为 "null resource id" 传递是安全的,因为 View
的 setBackgroundResource
方法会检查 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;
}
您可以使用的一种方法是编写自定义 @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
,它必须 return 一个 ColorDrawable
而不是 Drawable
- - 否则内置方法将被视为更多 specific/appropriate.
另一种方法是在数据绑定表达式中使用静态方法将颜色转换为 Drawable
,以使值类型匹配。例如,您可以导入内置 Converters
class:
<data>
<import type="androidx.databinding.adapters.Converters"/>
</data>
...然后这样写你的表达式:
android:background="@{article.sponsored ? Converters.convertColorToDrawable(@color/sponsored_article_background) : null}"
...虽然我个人建议将这种条件逻辑放在数据绑定适配器方法中,例如使用 getArticleBackground()
方法 return 是 Drawable 或 null。一般来说,如果您避免在布局文件中放置决策逻辑,事情会更容易调试和跟踪。
试试这个:
@Bindable
private int color;
并在构造函数中
color = Color.parseColor("your color in String for examp.(#ffffff)")
在xml中:
android:textColor = "@{data.color}"
在这个 article 中,您可以找到两个很好的解决方案,但就我而言,只有一个可行,因为我想更改 Material 按钮中的背景色调,这是我的绑定适配器:
首先,创建一个 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}" />
它真的很旧 post 但我想再推荐一个解决方案。
- 在绘图中声明自定义 STYLES/BACKGROUNDS**
我有四种相似的样式,所以我只粘贴其中一种。
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/colorAccent">
<item>
<layer-list>
<item>
<shape android:shape="oval">
<stroke
android:width="1dp"
android:color="@color/colorAccent" />
<solid android:color="@color/colorPrimaryDark" />
<size
android:width="200dp"
android:height="200dp" />
</shape>
</item>
<item
android:width="100dp"
android:height="100dp"
android:gravity="center"
android:drawable="@drawable/ic_car_white" />
</layer-list>
</item>
</ripple>
当我设置此样式时,我的按钮如下所示:
- 在 model/handler class 中准备可绑定值
在我的例子中,我在 ActivityMainEventHandler class
中有以下代码@Bindable
public Drawable getConenctButtonStyle() {
// here i'm checking connection state but you can do own conditions
ConnectionState state = Communication.getInstance().getConnectionState();
if (state != null) switch (state) {
case CONNECTED:
return ctx.getDrawable(R.drawable.circle_btn_state_green);
case CONNECTING:
case DISCONNECTING:
return ctx.getDrawable(R.drawable.circle_btn_state_orange);
case DISCONNECTED:
return ctx.getDrawable(R.drawable.circle_btn_state_red);
}
return ctx.getDrawable(R.drawable.circle_btn_state_first);
}
- 将您的 class 传递给您的视图
Activity onCreate:
bind = DataBindingUtil.setContentView(this, R.layout.activity_main);
handler = new ActivityMainEventHandler(this, bind);
bind.setMainHandler(handler);
我们的 XML activity
<data>
<variable
name="mainHandler"
type="xx.xxx.packagename.eventHandlers.ActivityMainEventHandler" />
</data>
- 将您的背景设置为如下所示
标记:
android:background="@{mainHandler.conenctButtonStyle}"
- 然后如果你想再次检查步骤 2 的条件并重绘视图,只需调用
代码:
//BR.conenctButtonStyle it's automatically generated id
bind.notifyPropertyChanged(BR.conenctButtonStyle);
另一个解决方案,如果你只想设置 backgroundTint
,而不是整个 background
,你可以这样使用它:
如果您的最小 api 是 21,您将需要导入 ContextCompat
:
<import type="androidx.core.content.ContextCompat" />
...
app:backgroundTintList="@{ContextCompat.getColorStateList(context, [funtion_to_get_your_color_res_id]))}"