How Do We Get SpannedString Objects From String Resources Using Data Binding?












10














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:




  • Get the SpannedString corresponding to my string resource (i.e., what you get from getText() instead of getString()), or

  • Get the string resource ID of my string resource, so I can call getText() myself to get my SpannedString










share|improve this question
























  • What is the attr type for thingy?
    – Ben P.
    Nov 1 '18 at 18:15










  • @BenP.: There is no attr for thingy. When the data binding compiler encounters a data binding expression, it looks for binding adapters based on the attribute name.
    – CommonsWare
    Nov 1 '18 at 18:18












  • Hm, ok. I'm not too familiar with data binding... so how does the system "know" the type to expect? Do you have a String getThingy() method?
    – Ben P.
    Nov 1 '18 at 18:22










  • Well, I thought it used reflection to examine the method/function signature of the binding adapter implementation. It seems to do that in other places. In the above code snippets, the first parameter is the type of the widget you are using the binding expression on (TextView) and the second parameter is the type of the attribute value from the binding expression (Int, String, etc.). Perhaps it is just not that flexible when it comes to string resources. That's kinda the point of this question.
    – CommonsWare
    Nov 1 '18 at 18:25








  • 2




    i'm afraid the only thing you could do is to pass a "my_annotated_string" string to your handleThingy adapter and use infamous Resources#getIdentifier() method
    – pskink
    Nov 1 '18 at 18:35
















10














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:




  • Get the SpannedString corresponding to my string resource (i.e., what you get from getText() instead of getString()), or

  • Get the string resource ID of my string resource, so I can call getText() myself to get my SpannedString










share|improve this question
























  • What is the attr type for thingy?
    – Ben P.
    Nov 1 '18 at 18:15










  • @BenP.: There is no attr for thingy. When the data binding compiler encounters a data binding expression, it looks for binding adapters based on the attribute name.
    – CommonsWare
    Nov 1 '18 at 18:18












  • Hm, ok. I'm not too familiar with data binding... so how does the system "know" the type to expect? Do you have a String getThingy() method?
    – Ben P.
    Nov 1 '18 at 18:22










  • Well, I thought it used reflection to examine the method/function signature of the binding adapter implementation. It seems to do that in other places. In the above code snippets, the first parameter is the type of the widget you are using the binding expression on (TextView) and the second parameter is the type of the attribute value from the binding expression (Int, String, etc.). Perhaps it is just not that flexible when it comes to string resources. That's kinda the point of this question.
    – CommonsWare
    Nov 1 '18 at 18:25








  • 2




    i'm afraid the only thing you could do is to pass a "my_annotated_string" string to your handleThingy adapter and use infamous Resources#getIdentifier() method
    – pskink
    Nov 1 '18 at 18:35














10












10








10


1





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:




  • Get the SpannedString corresponding to my string resource (i.e., what you get from getText() instead of getString()), or

  • Get the string resource ID of my string resource, so I can call getText() myself to get my SpannedString










share|improve this question















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:




  • Get the SpannedString corresponding to my string resource (i.e., what you get from getText() instead of getString()), or

  • Get the string resource ID of my string resource, so I can call getText() myself to get my SpannedString







android android-resources android-databinding






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 1 '18 at 21:14

























asked Nov 1 '18 at 18:07









CommonsWare

764k13818641915




764k13818641915












  • What is the attr type for thingy?
    – Ben P.
    Nov 1 '18 at 18:15










  • @BenP.: There is no attr for thingy. When the data binding compiler encounters a data binding expression, it looks for binding adapters based on the attribute name.
    – CommonsWare
    Nov 1 '18 at 18:18












  • Hm, ok. I'm not too familiar with data binding... so how does the system "know" the type to expect? Do you have a String getThingy() method?
    – Ben P.
    Nov 1 '18 at 18:22










  • Well, I thought it used reflection to examine the method/function signature of the binding adapter implementation. It seems to do that in other places. In the above code snippets, the first parameter is the type of the widget you are using the binding expression on (TextView) and the second parameter is the type of the attribute value from the binding expression (Int, String, etc.). Perhaps it is just not that flexible when it comes to string resources. That's kinda the point of this question.
    – CommonsWare
    Nov 1 '18 at 18:25








  • 2




    i'm afraid the only thing you could do is to pass a "my_annotated_string" string to your handleThingy adapter and use infamous Resources#getIdentifier() method
    – pskink
    Nov 1 '18 at 18:35


















  • What is the attr type for thingy?
    – Ben P.
    Nov 1 '18 at 18:15










  • @BenP.: There is no attr for thingy. When the data binding compiler encounters a data binding expression, it looks for binding adapters based on the attribute name.
    – CommonsWare
    Nov 1 '18 at 18:18












  • Hm, ok. I'm not too familiar with data binding... so how does the system "know" the type to expect? Do you have a String getThingy() method?
    – Ben P.
    Nov 1 '18 at 18:22










  • Well, I thought it used reflection to examine the method/function signature of the binding adapter implementation. It seems to do that in other places. In the above code snippets, the first parameter is the type of the widget you are using the binding expression on (TextView) and the second parameter is the type of the attribute value from the binding expression (Int, String, etc.). Perhaps it is just not that flexible when it comes to string resources. That's kinda the point of this question.
    – CommonsWare
    Nov 1 '18 at 18:25








  • 2




    i'm afraid the only thing you could do is to pass a "my_annotated_string" string to your handleThingy adapter and use infamous Resources#getIdentifier() method
    – pskink
    Nov 1 '18 at 18:35
















What is the attr type for thingy?
– Ben P.
Nov 1 '18 at 18:15




What is the attr type for thingy?
– Ben P.
Nov 1 '18 at 18:15












@BenP.: There is no attr for thingy. When the data binding compiler encounters a data binding expression, it looks for binding adapters based on the attribute name.
– CommonsWare
Nov 1 '18 at 18:18






@BenP.: There is no attr for thingy. When the data binding compiler encounters a data binding expression, it looks for binding adapters based on the attribute name.
– CommonsWare
Nov 1 '18 at 18:18














Hm, ok. I'm not too familiar with data binding... so how does the system "know" the type to expect? Do you have a String getThingy() method?
– Ben P.
Nov 1 '18 at 18:22




Hm, ok. I'm not too familiar with data binding... so how does the system "know" the type to expect? Do you have a String getThingy() method?
– Ben P.
Nov 1 '18 at 18:22












Well, I thought it used reflection to examine the method/function signature of the binding adapter implementation. It seems to do that in other places. In the above code snippets, the first parameter is the type of the widget you are using the binding expression on (TextView) and the second parameter is the type of the attribute value from the binding expression (Int, String, etc.). Perhaps it is just not that flexible when it comes to string resources. That's kinda the point of this question.
– CommonsWare
Nov 1 '18 at 18:25






Well, I thought it used reflection to examine the method/function signature of the binding adapter implementation. It seems to do that in other places. In the above code snippets, the first parameter is the type of the widget you are using the binding expression on (TextView) and the second parameter is the type of the attribute value from the binding expression (Int, String, etc.). Perhaps it is just not that flexible when it comes to string resources. That's kinda the point of this question.
– CommonsWare
Nov 1 '18 at 18:25






2




2




i'm afraid the only thing you could do is to pass a "my_annotated_string" string to your handleThingy adapter and use infamous Resources#getIdentifier() method
– pskink
Nov 1 '18 at 18:35




i'm afraid the only thing you could do is to pass a "my_annotated_string" string to your handleThingy adapter and use infamous Resources#getIdentifier() method
– pskink
Nov 1 '18 at 18:35












4 Answers
4






active

oldest

votes


















4





+125









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"/>





share|improve this answer





























    3














    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.






    share|improve this answer























    • That's an interesting approach -- thanks! IMHO, it is more complicated than what I had suggested in the question comments, which is more or less what tynn has in another answer.
      – CommonsWare
      Nov 5 '18 at 22:56










    • @CommonsWare Maybe, but it is just an introduction of a TypedArray which is in the data binding universe as is the entire solution - nothing odd. Multiple strings, if needed, can be collected in the same array.
      – Cheticamp
      Nov 5 '18 at 23:01



















    0














    Use this binding adapter:



    @BindingAdapter("thingy")
    fun handleThingy(textView: TextView, @StringRes thingy: Int) { /* stuff goes here */ }


    and instead of using app:thingy="@{@string/my_annotated_string}" pass resource into binding: thingy="@string/my_annotated_string" (without app:).






    share|improve this answer

















    • 1




      Thanks! However, this does not work. The binding adapter does not get invoked. AFAIK, data binding only works on attributes with binding expressions. Tested on AS 3.2.1.
      – CommonsWare
      Nov 8 '18 at 12:15



















    0














    one can define just any data-type with <import>:



    <data>
    <import type="android.util.SparseArray"/>
    <import type="java.util.Map"/>
    <import type="java.util.List"/>
    <variable name="list" type="List<String>"/>
    <variable name="sparse" type="SparseArray<String>"/>
    <variable name="map" type="Map<String, String>"/>
    <variable name="index" type="int"/>
    <variable name="key" type="String"/>
    </data>


    and then obtain the text from it:



    android:text="@{sparse[index]}"


    to define a thingy with data-type SpannedString (or one that extends SpannedString):



    <data>
    <import type="com.acme.model.BindableSpannedString"/>
    <variable name="thingy" type="BindableSpannedString"/>
    <variable name="index" type="int"/>
    </data>


    the getter annotated with @Bindable should be index access:



    android:text="@{thingy.getSpanAt(index)}"


    SpannedString lacks the getter required to bind it, nor can one add annotations to framework classes.






    share|improve this answer























    • "the name of the method matters" -- no, it does not. For example, in the documentation that you linked to, it shows a loadImage() function as a @BindingAdapter. Thanks, though!
      – CommonsWare
      Nov 10 '18 at 14:14












    • @CommonsWare that example uses app: namespace (the default demands @Bindable getters and setters, which notify). the @BindingConversion annotation might probably be suitable for the use case, hence there is some kind of conversion required.
      – Martin Zeitler
      Nov 10 '18 at 15:07












    • "that example uses app: namespace" -- that is my use case as well. For example, thingy is not a standard Android attribute. Your point about @BindingConversion is interesting -- I might be able to set one up that maps a string resource Int to a SpannedString and address the problem that way. Thanks!
      – CommonsWare
      Nov 10 '18 at 15:20










    • @CommonsWare extended on my answer, because generally it's a custom data-type. developer.android.com/reference/android/text/SpannedString does not really have any getter for a span at an index. adding some class BindableSpannedString extends SpannedString in order add @Bindable getters should make the data-binding work - and nested values accessible.
      – Martin Zeitler
      Nov 12 '18 at 11:57












    • Thanks, but I am not looking for nested values. I am looking for a binding adapter to be handed the spanned value (as a Spannable, SpannedString, whatever) for processing in the binding adapter.
      – CommonsWare
      Nov 12 '18 at 12:33











    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53106939%2fhow-do-we-get-spannedstring-objects-from-string-resources-using-data-binding%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    4 Answers
    4






    active

    oldest

    votes








    4 Answers
    4






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    4





    +125









    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"/>





    share|improve this answer


























      4





      +125









      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"/>





      share|improve this answer
























        4





        +125







        4





        +125



        4




        +125




        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"/>





        share|improve this answer












        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"/>






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 4 '18 at 21:14









        tynn

        19.4k54375




        19.4k54375

























            3














            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.






            share|improve this answer























            • That's an interesting approach -- thanks! IMHO, it is more complicated than what I had suggested in the question comments, which is more or less what tynn has in another answer.
              – CommonsWare
              Nov 5 '18 at 22:56










            • @CommonsWare Maybe, but it is just an introduction of a TypedArray which is in the data binding universe as is the entire solution - nothing odd. Multiple strings, if needed, can be collected in the same array.
              – Cheticamp
              Nov 5 '18 at 23:01
















            3














            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.






            share|improve this answer























            • That's an interesting approach -- thanks! IMHO, it is more complicated than what I had suggested in the question comments, which is more or less what tynn has in another answer.
              – CommonsWare
              Nov 5 '18 at 22:56










            • @CommonsWare Maybe, but it is just an introduction of a TypedArray which is in the data binding universe as is the entire solution - nothing odd. Multiple strings, if needed, can be collected in the same array.
              – Cheticamp
              Nov 5 '18 at 23:01














            3












            3








            3






            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.






            share|improve this answer














            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.







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Nov 5 '18 at 18:19

























            answered Nov 5 '18 at 18:07









            Cheticamp

            25.6k42859




            25.6k42859












            • That's an interesting approach -- thanks! IMHO, it is more complicated than what I had suggested in the question comments, which is more or less what tynn has in another answer.
              – CommonsWare
              Nov 5 '18 at 22:56










            • @CommonsWare Maybe, but it is just an introduction of a TypedArray which is in the data binding universe as is the entire solution - nothing odd. Multiple strings, if needed, can be collected in the same array.
              – Cheticamp
              Nov 5 '18 at 23:01


















            • That's an interesting approach -- thanks! IMHO, it is more complicated than what I had suggested in the question comments, which is more or less what tynn has in another answer.
              – CommonsWare
              Nov 5 '18 at 22:56










            • @CommonsWare Maybe, but it is just an introduction of a TypedArray which is in the data binding universe as is the entire solution - nothing odd. Multiple strings, if needed, can be collected in the same array.
              – Cheticamp
              Nov 5 '18 at 23:01
















            That's an interesting approach -- thanks! IMHO, it is more complicated than what I had suggested in the question comments, which is more or less what tynn has in another answer.
            – CommonsWare
            Nov 5 '18 at 22:56




            That's an interesting approach -- thanks! IMHO, it is more complicated than what I had suggested in the question comments, which is more or less what tynn has in another answer.
            – CommonsWare
            Nov 5 '18 at 22:56












            @CommonsWare Maybe, but it is just an introduction of a TypedArray which is in the data binding universe as is the entire solution - nothing odd. Multiple strings, if needed, can be collected in the same array.
            – Cheticamp
            Nov 5 '18 at 23:01




            @CommonsWare Maybe, but it is just an introduction of a TypedArray which is in the data binding universe as is the entire solution - nothing odd. Multiple strings, if needed, can be collected in the same array.
            – Cheticamp
            Nov 5 '18 at 23:01











            0














            Use this binding adapter:



            @BindingAdapter("thingy")
            fun handleThingy(textView: TextView, @StringRes thingy: Int) { /* stuff goes here */ }


            and instead of using app:thingy="@{@string/my_annotated_string}" pass resource into binding: thingy="@string/my_annotated_string" (without app:).






            share|improve this answer

















            • 1




              Thanks! However, this does not work. The binding adapter does not get invoked. AFAIK, data binding only works on attributes with binding expressions. Tested on AS 3.2.1.
              – CommonsWare
              Nov 8 '18 at 12:15
















            0














            Use this binding adapter:



            @BindingAdapter("thingy")
            fun handleThingy(textView: TextView, @StringRes thingy: Int) { /* stuff goes here */ }


            and instead of using app:thingy="@{@string/my_annotated_string}" pass resource into binding: thingy="@string/my_annotated_string" (without app:).






            share|improve this answer

















            • 1




              Thanks! However, this does not work. The binding adapter does not get invoked. AFAIK, data binding only works on attributes with binding expressions. Tested on AS 3.2.1.
              – CommonsWare
              Nov 8 '18 at 12:15














            0












            0








            0






            Use this binding adapter:



            @BindingAdapter("thingy")
            fun handleThingy(textView: TextView, @StringRes thingy: Int) { /* stuff goes here */ }


            and instead of using app:thingy="@{@string/my_annotated_string}" pass resource into binding: thingy="@string/my_annotated_string" (without app:).






            share|improve this answer












            Use this binding adapter:



            @BindingAdapter("thingy")
            fun handleThingy(textView: TextView, @StringRes thingy: Int) { /* stuff goes here */ }


            and instead of using app:thingy="@{@string/my_annotated_string}" pass resource into binding: thingy="@string/my_annotated_string" (without app:).







            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered Nov 8 '18 at 10:11









            Matt Twig

            23115




            23115








            • 1




              Thanks! However, this does not work. The binding adapter does not get invoked. AFAIK, data binding only works on attributes with binding expressions. Tested on AS 3.2.1.
              – CommonsWare
              Nov 8 '18 at 12:15














            • 1




              Thanks! However, this does not work. The binding adapter does not get invoked. AFAIK, data binding only works on attributes with binding expressions. Tested on AS 3.2.1.
              – CommonsWare
              Nov 8 '18 at 12:15








            1




            1




            Thanks! However, this does not work. The binding adapter does not get invoked. AFAIK, data binding only works on attributes with binding expressions. Tested on AS 3.2.1.
            – CommonsWare
            Nov 8 '18 at 12:15




            Thanks! However, this does not work. The binding adapter does not get invoked. AFAIK, data binding only works on attributes with binding expressions. Tested on AS 3.2.1.
            – CommonsWare
            Nov 8 '18 at 12:15











            0














            one can define just any data-type with <import>:



            <data>
            <import type="android.util.SparseArray"/>
            <import type="java.util.Map"/>
            <import type="java.util.List"/>
            <variable name="list" type="List<String>"/>
            <variable name="sparse" type="SparseArray<String>"/>
            <variable name="map" type="Map<String, String>"/>
            <variable name="index" type="int"/>
            <variable name="key" type="String"/>
            </data>


            and then obtain the text from it:



            android:text="@{sparse[index]}"


            to define a thingy with data-type SpannedString (or one that extends SpannedString):



            <data>
            <import type="com.acme.model.BindableSpannedString"/>
            <variable name="thingy" type="BindableSpannedString"/>
            <variable name="index" type="int"/>
            </data>


            the getter annotated with @Bindable should be index access:



            android:text="@{thingy.getSpanAt(index)}"


            SpannedString lacks the getter required to bind it, nor can one add annotations to framework classes.






            share|improve this answer























            • "the name of the method matters" -- no, it does not. For example, in the documentation that you linked to, it shows a loadImage() function as a @BindingAdapter. Thanks, though!
              – CommonsWare
              Nov 10 '18 at 14:14












            • @CommonsWare that example uses app: namespace (the default demands @Bindable getters and setters, which notify). the @BindingConversion annotation might probably be suitable for the use case, hence there is some kind of conversion required.
              – Martin Zeitler
              Nov 10 '18 at 15:07












            • "that example uses app: namespace" -- that is my use case as well. For example, thingy is not a standard Android attribute. Your point about @BindingConversion is interesting -- I might be able to set one up that maps a string resource Int to a SpannedString and address the problem that way. Thanks!
              – CommonsWare
              Nov 10 '18 at 15:20










            • @CommonsWare extended on my answer, because generally it's a custom data-type. developer.android.com/reference/android/text/SpannedString does not really have any getter for a span at an index. adding some class BindableSpannedString extends SpannedString in order add @Bindable getters should make the data-binding work - and nested values accessible.
              – Martin Zeitler
              Nov 12 '18 at 11:57












            • Thanks, but I am not looking for nested values. I am looking for a binding adapter to be handed the spanned value (as a Spannable, SpannedString, whatever) for processing in the binding adapter.
              – CommonsWare
              Nov 12 '18 at 12:33
















            0














            one can define just any data-type with <import>:



            <data>
            <import type="android.util.SparseArray"/>
            <import type="java.util.Map"/>
            <import type="java.util.List"/>
            <variable name="list" type="List<String>"/>
            <variable name="sparse" type="SparseArray<String>"/>
            <variable name="map" type="Map<String, String>"/>
            <variable name="index" type="int"/>
            <variable name="key" type="String"/>
            </data>


            and then obtain the text from it:



            android:text="@{sparse[index]}"


            to define a thingy with data-type SpannedString (or one that extends SpannedString):



            <data>
            <import type="com.acme.model.BindableSpannedString"/>
            <variable name="thingy" type="BindableSpannedString"/>
            <variable name="index" type="int"/>
            </data>


            the getter annotated with @Bindable should be index access:



            android:text="@{thingy.getSpanAt(index)}"


            SpannedString lacks the getter required to bind it, nor can one add annotations to framework classes.






            share|improve this answer























            • "the name of the method matters" -- no, it does not. For example, in the documentation that you linked to, it shows a loadImage() function as a @BindingAdapter. Thanks, though!
              – CommonsWare
              Nov 10 '18 at 14:14












            • @CommonsWare that example uses app: namespace (the default demands @Bindable getters and setters, which notify). the @BindingConversion annotation might probably be suitable for the use case, hence there is some kind of conversion required.
              – Martin Zeitler
              Nov 10 '18 at 15:07












            • "that example uses app: namespace" -- that is my use case as well. For example, thingy is not a standard Android attribute. Your point about @BindingConversion is interesting -- I might be able to set one up that maps a string resource Int to a SpannedString and address the problem that way. Thanks!
              – CommonsWare
              Nov 10 '18 at 15:20










            • @CommonsWare extended on my answer, because generally it's a custom data-type. developer.android.com/reference/android/text/SpannedString does not really have any getter for a span at an index. adding some class BindableSpannedString extends SpannedString in order add @Bindable getters should make the data-binding work - and nested values accessible.
              – Martin Zeitler
              Nov 12 '18 at 11:57












            • Thanks, but I am not looking for nested values. I am looking for a binding adapter to be handed the spanned value (as a Spannable, SpannedString, whatever) for processing in the binding adapter.
              – CommonsWare
              Nov 12 '18 at 12:33














            0












            0








            0






            one can define just any data-type with <import>:



            <data>
            <import type="android.util.SparseArray"/>
            <import type="java.util.Map"/>
            <import type="java.util.List"/>
            <variable name="list" type="List<String>"/>
            <variable name="sparse" type="SparseArray<String>"/>
            <variable name="map" type="Map<String, String>"/>
            <variable name="index" type="int"/>
            <variable name="key" type="String"/>
            </data>


            and then obtain the text from it:



            android:text="@{sparse[index]}"


            to define a thingy with data-type SpannedString (or one that extends SpannedString):



            <data>
            <import type="com.acme.model.BindableSpannedString"/>
            <variable name="thingy" type="BindableSpannedString"/>
            <variable name="index" type="int"/>
            </data>


            the getter annotated with @Bindable should be index access:



            android:text="@{thingy.getSpanAt(index)}"


            SpannedString lacks the getter required to bind it, nor can one add annotations to framework classes.






            share|improve this answer














            one can define just any data-type with <import>:



            <data>
            <import type="android.util.SparseArray"/>
            <import type="java.util.Map"/>
            <import type="java.util.List"/>
            <variable name="list" type="List<String>"/>
            <variable name="sparse" type="SparseArray<String>"/>
            <variable name="map" type="Map<String, String>"/>
            <variable name="index" type="int"/>
            <variable name="key" type="String"/>
            </data>


            and then obtain the text from it:



            android:text="@{sparse[index]}"


            to define a thingy with data-type SpannedString (or one that extends SpannedString):



            <data>
            <import type="com.acme.model.BindableSpannedString"/>
            <variable name="thingy" type="BindableSpannedString"/>
            <variable name="index" type="int"/>
            </data>


            the getter annotated with @Bindable should be index access:



            android:text="@{thingy.getSpanAt(index)}"


            SpannedString lacks the getter required to bind it, nor can one add annotations to framework classes.







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Nov 12 '18 at 12:12

























            answered Nov 10 '18 at 13:54









            Martin Zeitler

            14.3k33863




            14.3k33863












            • "the name of the method matters" -- no, it does not. For example, in the documentation that you linked to, it shows a loadImage() function as a @BindingAdapter. Thanks, though!
              – CommonsWare
              Nov 10 '18 at 14:14












            • @CommonsWare that example uses app: namespace (the default demands @Bindable getters and setters, which notify). the @BindingConversion annotation might probably be suitable for the use case, hence there is some kind of conversion required.
              – Martin Zeitler
              Nov 10 '18 at 15:07












            • "that example uses app: namespace" -- that is my use case as well. For example, thingy is not a standard Android attribute. Your point about @BindingConversion is interesting -- I might be able to set one up that maps a string resource Int to a SpannedString and address the problem that way. Thanks!
              – CommonsWare
              Nov 10 '18 at 15:20










            • @CommonsWare extended on my answer, because generally it's a custom data-type. developer.android.com/reference/android/text/SpannedString does not really have any getter for a span at an index. adding some class BindableSpannedString extends SpannedString in order add @Bindable getters should make the data-binding work - and nested values accessible.
              – Martin Zeitler
              Nov 12 '18 at 11:57












            • Thanks, but I am not looking for nested values. I am looking for a binding adapter to be handed the spanned value (as a Spannable, SpannedString, whatever) for processing in the binding adapter.
              – CommonsWare
              Nov 12 '18 at 12:33


















            • "the name of the method matters" -- no, it does not. For example, in the documentation that you linked to, it shows a loadImage() function as a @BindingAdapter. Thanks, though!
              – CommonsWare
              Nov 10 '18 at 14:14












            • @CommonsWare that example uses app: namespace (the default demands @Bindable getters and setters, which notify). the @BindingConversion annotation might probably be suitable for the use case, hence there is some kind of conversion required.
              – Martin Zeitler
              Nov 10 '18 at 15:07












            • "that example uses app: namespace" -- that is my use case as well. For example, thingy is not a standard Android attribute. Your point about @BindingConversion is interesting -- I might be able to set one up that maps a string resource Int to a SpannedString and address the problem that way. Thanks!
              – CommonsWare
              Nov 10 '18 at 15:20










            • @CommonsWare extended on my answer, because generally it's a custom data-type. developer.android.com/reference/android/text/SpannedString does not really have any getter for a span at an index. adding some class BindableSpannedString extends SpannedString in order add @Bindable getters should make the data-binding work - and nested values accessible.
              – Martin Zeitler
              Nov 12 '18 at 11:57












            • Thanks, but I am not looking for nested values. I am looking for a binding adapter to be handed the spanned value (as a Spannable, SpannedString, whatever) for processing in the binding adapter.
              – CommonsWare
              Nov 12 '18 at 12:33
















            "the name of the method matters" -- no, it does not. For example, in the documentation that you linked to, it shows a loadImage() function as a @BindingAdapter. Thanks, though!
            – CommonsWare
            Nov 10 '18 at 14:14






            "the name of the method matters" -- no, it does not. For example, in the documentation that you linked to, it shows a loadImage() function as a @BindingAdapter. Thanks, though!
            – CommonsWare
            Nov 10 '18 at 14:14














            @CommonsWare that example uses app: namespace (the default demands @Bindable getters and setters, which notify). the @BindingConversion annotation might probably be suitable for the use case, hence there is some kind of conversion required.
            – Martin Zeitler
            Nov 10 '18 at 15:07






            @CommonsWare that example uses app: namespace (the default demands @Bindable getters and setters, which notify). the @BindingConversion annotation might probably be suitable for the use case, hence there is some kind of conversion required.
            – Martin Zeitler
            Nov 10 '18 at 15:07














            "that example uses app: namespace" -- that is my use case as well. For example, thingy is not a standard Android attribute. Your point about @BindingConversion is interesting -- I might be able to set one up that maps a string resource Int to a SpannedString and address the problem that way. Thanks!
            – CommonsWare
            Nov 10 '18 at 15:20




            "that example uses app: namespace" -- that is my use case as well. For example, thingy is not a standard Android attribute. Your point about @BindingConversion is interesting -- I might be able to set one up that maps a string resource Int to a SpannedString and address the problem that way. Thanks!
            – CommonsWare
            Nov 10 '18 at 15:20












            @CommonsWare extended on my answer, because generally it's a custom data-type. developer.android.com/reference/android/text/SpannedString does not really have any getter for a span at an index. adding some class BindableSpannedString extends SpannedString in order add @Bindable getters should make the data-binding work - and nested values accessible.
            – Martin Zeitler
            Nov 12 '18 at 11:57






            @CommonsWare extended on my answer, because generally it's a custom data-type. developer.android.com/reference/android/text/SpannedString does not really have any getter for a span at an index. adding some class BindableSpannedString extends SpannedString in order add @Bindable getters should make the data-binding work - and nested values accessible.
            – Martin Zeitler
            Nov 12 '18 at 11:57














            Thanks, but I am not looking for nested values. I am looking for a binding adapter to be handed the spanned value (as a Spannable, SpannedString, whatever) for processing in the binding adapter.
            – CommonsWare
            Nov 12 '18 at 12:33




            Thanks, but I am not looking for nested values. I am looking for a binding adapter to be handed the spanned value (as a Spannable, SpannedString, whatever) for processing in the binding adapter.
            – CommonsWare
            Nov 12 '18 at 12:33


















            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53106939%2fhow-do-we-get-spannedstring-objects-from-string-resources-using-data-binding%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Florida Star v. B. J. F.

            Danny Elfman

            Lugert, Oklahoma