Binding Texts With Charm
Easier binding of texts for data binding library
I’m sure everyone using data binding in her/his application has already bound some texts into TextView or Button or any other View responsible for displaying textual information to the user.
I’m also quite sure most of you have already encountered situation when the final text was a result of some conditional flow or even result of some remote response (otherwise you wouldn’t really require binding, would you? 😃). In situation like that you can be confronted with a reality that you sometimes have String
data and sometimes you have Int
data (resource id) to display in that one and only View.
The Problem
Translations and internationalization asides, the example easy to imagine can be a situation where you are receiving some text from a remote API. This text is of course not part of your APK’s resources so you’ll end up with some String
or some CharSequence
in general. This is fine for happy path but there are situations when not everything works as you would imagine. There could be error situations or the user is just simply offline.
Then you would most likely want to display an error message, the one which is (hopefully) in your string.xml
file(s). This would be referenced as an Int
of course, not a String
. You can use Context
to load the content of that resource to String
representation but if you are using some modern architecture (and with binding enabled you probably are 😉) you would argue that you don’t have any Context
around, for example, in a standard ViewModel
.
And that is actually a good thing as we all know that Context
, the all-mighty god object of Android is rather tedious to keep around…
… no thank you! The good thing is that TextView
and its many child classes can work with both String
and resource Int
id. But if you try to somehow bind that to your view it won’t work out of the box even though the methods have same names.
@android.view.RemotableViewMethod
public final void setText(CharSequence text) {
...
}@android.view.RemotableViewMethod
public final void setText(@StringRes int resid) {
...
}
The compiler just can’t magically call the one with Int
parameter and other times the one with String
.
The problem is not difficult to solve but most of the solutions are ugly and verbose. Raging from defining multiple variables in view models to having custom binding adapters or even having multiple views in your layout 😱. But there are some good news… There is (at least one) library for that!🎉
The Library
DynamicText library which is part of Dynamic Components project can easily solve this problem with next to none verbosity or code changes to your already existing binding code.
The usual code in layout’s xml can often look similar to this
As you can see viewmodel
’s property title
is bound to TextView
. You would expect that if viewmodel.title
is returning String
(or some observable type of String
) everything would just work fine as this code would call TextView
‘s setText
method under the hood displaying your title to the user. It would also fail spectacularly if that method or variable was actually Int
.
Let’s see how it can look in case of the use of DynamicText library
The main point here is the val
property title
. It is not String
nor Int
but a DynamicText
type coming from the library. Later in the code you can easily assign a value to this (in-this-case-LiveData-but-that-is-not-requirement) property with factory functions conveniently provided for you by the library.
Only defining your variable as DynamicText
and assigning the right value and you are good to go. The code will compile without any further change needed. Binding code is not required to change at all. You are free to use Int
or String
, it doesn’t matter. You can even use formatted String
like
DynamicText.from(
"Formatted Text with %d and %.4f and %s",
45,
0.345,
"everything"
)
or
DynamicText.from(
R.string.formatted_string_with_two_ints_and_a_string,
45,
0.345,
"everything"
)
How is that possible? The library takes care of binding logic and processing of the string exactly the way how would you expect it to do and in the standard manner fully compatible with data binding library. No need to write your own binding adapters or helpers.
It’s only a cherry on top that plurals are also supported out of the box if you need them.
DynamicText.plural(R.plurals.months_no_number, Quantity(quantity))
This will produce “1 month” or “5 months” and can be bound to your view in no time! 🙌
That’s about it
If you would like more info visit project’s GitHub to see how to include it in your project and use it. There is even more dynamic components coming in the near future.
Happy coding!