Florina Muntenescu wrote up a cool post about using <annotation>
in string resources for being able to have flexible markup that you can process in your app using custom spans. I am trying to leverage it in data binding, but I cannot quite figure out how to get a SpannedString
edition of the string resource from data binding.
In my layout, I have app:thingy="@{@string/my_annotated_string}"
as an attribute on a TextView
. I have a binding adapter set up to handle thingy
attributes. However, the data binding system seems to insist that my value is a String
.
I have tried:
@BindingAdapter("thingy")
@JvmStatic
fun handleThingy(textView: TextView, thingy: SpannedString) { /* stuff goes here */ }
and:
@BindingAdapter("thingy")
@JvmStatic
fun handleThingy(textView: TextView, thingy: Spanned) { /* stuff goes here */ }
and:
@BindingAdapter("thingy")
@JvmStatic
fun handleThingy(textView: TextView, @StringRes thingy: Int) { /* stuff goes here */ }
In all cases, I get Cannot find the setter for attribute 'app:thingy' with parameter type java.lang.String on android.widget.TextView
build errors.
If I use String
or CharSequence
for the thingy
parameter type, it builds, but then I get passed a String
and I do not have my annotation spans from the string resource.
So, how can I either:
SpannedString
corresponding to my string resource (i.e., what you get from getText()
instead of getString()
), orgetText()
myself to get my SpannedString
As an expression, @string/my_annotated_string
evaluates to a string. Eventhough it resembles a string resource reference in XML, it's actually only a String
value.
It would be nice to have a @text/my_annotated_string
version as well, but as of the documentation this is not available.
Instead you'd have to use the actual resource within your binding expression:
app:thingy="@{string.my_annotated_string}"
app:thingy="@{context.getText(string.my_annotated_string)}"
This is assuming the import of the string
class:
<import type="path.to.R.string"/>
Here is a maybe slightly less icky way:
Define the annotated string.
<string name="my_annotated_string">A <annotation font="title_emphasis">cool</annotation> annotation <annotation font="title_emphasis">thingy</annotation>.</string>
Place a reference to that string resource into a TypedArray
:
<resources>
<array name="annotated_text">
<item>@string/my_annotated_string</item>
</array>
</resources>
Reference the TypedArray
in the layout:
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:thingy="@{@typedArray/annotated_text}" />
Finally, set a BindingAdapter
to capture the SpannedString
with the annotations:
@BindingAdapter("thingy")
public static void setThingy(TextView textView, TypedArray strings) {
SpannedString ss = (SpannedString) strings.getText(0);
Object spans[] = ss.getSpans(0, ss.length(), Object.class);
}
Although a little involved, this works. If there are multiple strings, the array can be expanded.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With