DataBinding: How to get resource by dynamic id?

AndroidAndroid Databinding

Android Problem Overview


I know that it is possible to reference resources in layout by their resource id:

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

However, I would like to reference resource by id which is known only at runtime. As a simple example, imagine we have such model:

public class MyPOJO {

    public final int resourceId = R.string.helloWorld;

}

And now I need to use this value as a value in a format string. Let's call it

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

The most straightforward approach does not work:

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

This will just put integer value into placeholder (also it proves that I initialized my POJO correctly, so I'm not providing whole layout here).

I also tried using @BindingConversion, but it did not worked (which is actually expected, but I tried anyway) - int was still assigned to placeholder and binding method was not called.

How can I explicitly get resource by it's id in DataBinding library?

Android Solutions


Solution 1 - Android

Another solution is to create a custom @BindingAdapter for it.

@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)));
}

And then just provide the variables separately.

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

You could use an array if you need multiple arguments, but in my case, one was sufficient.

Solution 2 - Android

As of June 2016 this is possible in XML:

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

Solution 3 - Android

You can use:

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

Solution 4 - Android

I ended up creating my own method:

public class BindingUtils {

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

}

Declaring an import for it:

<data>

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

    ...

</data>

And just calling it during binding:

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

Would be nice to have out-of-the-box method for that. DataBinding is sitll in Beta - so maybe it will come in future.

Solution 5 - Android

Another solution if you already have Context defined in your xml then you will not need to import String class.

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

will work for

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

If you don't have context in your xml. then follow this

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

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

and in your Activity

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

Solution 6 - Android

You can make use of automatic method selection described in the official documentation of binding adapters. The following description is taken from that document:

> 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.

With that in mind, you can implement your own binding adapter that accepts the ID of a string resource as an int argument.

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

This will allow you to use the standard android:text attribute to reference a string by its resource ID as well as its value.

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

Solution 7 - Android

Kotlin version:

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

in xml

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

Solution 8 - Android

you can use context inside your XML

 <TextView
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:text= "@{context.getString(myPojo.resourceId)}"
   />

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDmitry ZaytsevView Question on Stackoverflow
Solution 1 - AndroidJim PekarekView Answer on Stackoverflow
Solution 2 - AndroidKaskasiView Answer on Stackoverflow
Solution 3 - AndroidDalvinder SinghView Answer on Stackoverflow
Solution 4 - AndroidDmitry ZaytsevView Answer on Stackoverflow
Solution 5 - AndroidKhemraj SharmaView Answer on Stackoverflow
Solution 6 - AndroidSefa KeleşView Answer on Stackoverflow
Solution 7 - AndroidYazazzelloView Answer on Stackoverflow
Solution 8 - AndroidMomen ZaqoutView Answer on Stackoverflow