DataBinding:如何通过动态ID获取资源?

DataBinding: How to get resource by dynamic id?

我知道可以通过资源 ID 在布局中引用资源:

android:text="@{@string/resourceName}"

但是,我想通过仅在运行时才知道的 id 来引用资源。作为一个简单的例子,假设我们有这样的模型:

public class MyPOJO {

    public final int resourceId = R.string.helloWorld;

}

现在我需要将此值用作格式字符串中的值。让我们称之为

<string name="myFormatString">Value is: %s</string>

最直接的方法行不通:

android:text="@{@string/myFormatString(myPojo.resourceId)}"

这只会将整数值放入占位符中(这也证明我正确初始化了我的 POJO,所以我没有在这里提供整个布局)。

我也试过使用 @BindingConversion,但它没有用(这实际上是预期的,但我还是试过了)- int 仍然被分配给占位符并且没有调用绑定方法。

如何通过 DataBinding 库中的 ID 显式获取资源?

我最终创建了自己的方法:

public class BindingUtils {

    public static String string(int resourceId) {
        return MyApplication
                .getApplication()
                .getResources()
                .getString(resourceId);
    }

}

正在为其声明导入:

<data>

    <import type="com.example.BindingUtils" />

    ...

</data>

并且只是在绑定期间调用它:

android:text="@{@string/myFormatString(BindingUtils.string(myPojo.resourceId))}"

如果有开箱即用的方法就好了。 DataBinding 处于 Beta 阶段 - 所以也许将来会出现。

另一种解决方案是为其创建自定义 @BindingAdapter

@BindingAdapter({"format", "argId"})
public static void setFormattedText(TextView textView, String format, int argId){
    if(argId == 0) return;
    textView.setText(String.format(format, textView.getResources().getString(argId)));
}

然后单独提供变量即可。

<TextView
    app:format="@{@string/myFormatString}"
    app:argId="@{myPojo.resourceId}"

如果需要多个参数,可以使用数组,但在我的情况下,一个就足够了。

截至 2016 年 6 月,这在 XML 中是可能的:

android:text= "@{String.format(@string/my_format_string, myPojo.resourceId)}"

您可以使用:

android:text='@{(id > 0) ? context.getString(id) : ""}'

另一种解决方案,如果您 已经在 xml 中定义了 Context 那么您将 不需要导入 Stringclass。

android:text="@{@string/myFormatString(context.getString(pojo.res))}"

将适用于

<string name="myFormatString">Value is: %s</string>

如果您的 xml 中没有上下文。然后按照这个

<data>    
     <variable
         name="context"
         type="abc.UserActivity"/>

     <variable
         name="pojo"
         type="abc.MyPOJO"/>
 </data>

在你的 Activity

ActivityUserBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_user);
binding.setPojo(new MyPOJO());
binding.setContext(this);

Kotlin 版本:

@BindingAdapter("template", "resId")
fun TextView.setFormattedText(template: String, resId: Int) {
    if (template.isEmpty() || resId == 0) return
    text = template.format(resources.getString(resId))
}

在xml

<TextView
    app:template="@{@string/myFormatString}"
    app:resId="@{viewModel.resourceId}"/>

您可以使用绑定适配器官方文档中描述的automatic method selection。以下描述摘自该文档:

For an attribute named example, the library automatically tries to find the method setExample(arg) that accepts compatible types as the argument. The namespace of the attribute isn't considered, only the attribute name and type are used when searching for a method.

For example, given the android:text="@{user.name}" expression, the library looks for a setText(arg) method that accepts the type returned by user.getName(). If the return type of user.getName() is String, the library looks for a setText() method that accepts a String argument. If the expression returns an int instead, the library searches for a setText() method that accepts an int argument. The expression must return the correct type, you can cast the return value if necessary.

考虑到这一点,您可以实现自己的绑定适配器,它接受字符串资源的 ID 作为 int 参数。

@BindingAdapter("android:text")
fun setText(view: TextView, @StringRes resId: Int) {
    if (resId == 0) {
        view.text = null
    } else {
        view.setText(resId)
    }
}

这将允许您使用标准 android:text 属性通过资源 ID 及其值来引用字符串。

<TextView
    android:id="@+id/text_view_id"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{myPojo.resourceId}" />

您可以在 XML

中使用上下文
 <TextView
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:text= "@{context.getString(myPojo.resourceId)}"
   />