15

I want to make certain part of my text to bold whose value is set using DataBinding with ViewModel.

For e.g

If you are selected, you will pay $160 for your pair.

I am using strings resources

<string name="product_price">If you are selected, you will have to pay $%d for your pair.</string>

<TextView
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:layout_marginEnd="@dimen/spacing_xlarge"
          android:layout_marginStart="@dimen/spacing_xlarge"
          android:layout_marginBottom="@dimen/spacing_small"
          android:text="@{@string/product_price(productPrice)}"
          android:textColor="@color/button_tertiary"
          android:visibility="@{productPrice > 0}"
          style="@style/Body.Small"
          />

Currently passing product price using ViewModel with Binding by setting binding.setProductPrice(Object.getPrice())

I know the following solutions : But want to try using DataBinding

  • Using Html Text - But don't want to use it in code.
  • Using Different TextView in Horizontal Style. Setting styles as bold for that Product Price. - Really Bad Practice
  • Using SpannableString - But don't want to use it in code.

But all of the above solutions are workaround.

Question ::

Want to try DataBinding feature which can be used to style certain part of string. Just like SpannableString

Manipulate String in the Layout file using DataBinding

Vikalp Patel
  • 10,669
  • 6
  • 61
  • 96
  • @pskink : I don't want to pass full SpannableString or String to TextView. The idea is to apply textstyle to certain part of text. Though thanks for suggestion. – Vikalp Patel Aug 13 '18 at 12:41
  • @pskink : Yes, that's the last option(Passing SpannableString/SpannableStringBuilder). But can't we apply styling using DataBinding? – Vikalp Patel Aug 13 '18 at 12:47
  • @pskink : Returning either `SpannableStringBuilder` or `SpannableString` using DataBinding. But that's manipulation of String is in the code either java/kotlin. I want to manipulate `String` in the layout file using DataBinding. – Vikalp Patel Aug 13 '18 at 12:50
  • 1
    Why isn't this just a matter of adding `` tags to the string resource: `If you are selected, you will have to pay $%d for your pair.`? That won't work for arbitrary changes (e.g., foreground color), but for boldface it should be fine. "I want to manipulate String in the layout file using DataBinding." -- that's not really practical, as you do not have a full multi-statement programming language. *Some* stuff has to be in Java/Kotlin, at least at the level of a `BindingAdapter` or other helper code. – CommonsWare Aug 13 '18 at 12:53
  • @CommonsWare : Tried adding tags to the string resource: `If you are selected, you will have to pay $%d for your pair.`. But still the text is unformatted. Tried using `If you are selected, you will have to pay <![CDATA[$%d]]> for your pair`. Still it shows the same result, unformatted plain text. – Vikalp Patel Aug 13 '18 at 13:13

6 Answers6

7

As per @CommonsWare,

Tried by adding basic Html tag <string name="product_price">If you are selected, you will have to pay <![CDATA[<b>$%d</b>]]> for your pair.</string>

Layout File : Imported Html

 <?xml version="1.0" encoding="utf-8"?>
 <layout
   <data>
   <import type="android.text.Html"/>
   <data>
     <LinearLayout>
       <android.support.design.widget.CoordinatorLayout>
       <TextView
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:layout_marginEnd="@dimen/spacing_xlarge"
          android:layout_marginStart="@dimen/spacing_xlarge"
          android:layout_marginBottom="@dimen/spacing_small"
          android:text="@{Html.fromHtml(@string/product_price(productPrice))}"
          android:textColor="@color/button_tertiary"
          android:visibility="@{productPrice > 0}"
          style="@style/Body.Small"
          />
       </android.support.design.widget.CoordinatorLayout>
     </LinearLayout>
 </layout>
Vikalp Patel
  • 10,669
  • 6
  • 61
  • 96
  • @pskink : Yes, `Html.fromHtml` should be avoided. But I don't want to manipulate it in code. That's my end goal to achieve using DataBinding. Will update the answer as and when will find other best solution. – Vikalp Patel Aug 14 '18 at 03:57
  • 1
    @pskink : As of now I'm using `string` resources only `@string/xxxxx`. `android:text="@{Html.fromHtml(@string/product_price(productPrice))}"` – Vikalp Patel Aug 14 '18 at 08:10
  • `"But I don't want to manipulate it in code"` - and `Html.fromHtml` is not a java code? i already gave you a solution with simple `SpannableString` but still you think that `Html.fromHtm` does not require any java code and is better/faster than simple `setSpan` method - that's why i downvoted your answer – pskink Aug 14 '18 at 14:06
  • @pskink : Yes, `Html.fromHtml` internally runs java code for that. But I don't have to write java code what it does behind the scene, I less concerned with that as of now. But yes in future will definitely uses your solution. It's fine that you downvoted my answer. – Vikalp Patel Aug 16 '18 at 08:49
  • `"But I don't have to write java code what it does behind the scene"`, its just 3 lines of code (yes three) - creating a `StyleSpan`, setting span and calling `TextUtils#expandTemplate` - now compare it with what [Html.fromHtml](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/text/Html.java#228) is doing - of course the real job is done in [HtmlToSpannedConverter](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/text/Html.java#669) - but if you want your app to be terribly slow you can use it at will – pskink Aug 16 '18 at 08:56
  • this is an inefficeint solution. this is reducing code (yes) but increasing layout render time. – rbd_sqrl Nov 22 '19 at 08:39
7

You have to create a BindingAdapter and SpannableStringBuilder .

Binding Adapter

object Util {
    @BindingAdapter("main","secondText")
        @JvmStatic
        fun setBoldString(view: AppCompatTextView, maintext: String,sequence: String) {
            view.text = Util.getBoldText(maintext, sequence)
        }



    @JvmStatic
        fun getBoldText(text: String, name: String): SpannableStringBuilder {
            val str = SpannableStringBuilder(text)
            val textPosition = text.indexOf(name)
            str.setSpan(android.text.style.StyleSpan(Typeface.BOLD),
                    textPosition, textPosition + name.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
            return str
        }
}

XML

    <android.support.v7.widget.AppCompatTextView
        android:id="@+id/username"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:main="@{`you will pay $160 for your pair`}"
        app:secondText="@{`$160`}"
        android:textColor="@color/black"
        android:textSize="22sp" />

May be it helps you.

Mahavir Jain
  • 1,448
  • 10
  • 23
3

You can use a binding adapter coupled with SpannableString. Once you define the binding adapter, you can reuse it in all your layout files.

@BindingAdapter({"mainText", "priceToFormat"})
public static void format(TextView textView, String mainText, float 
      productPrice){
   //Use spannable string to format your text accordingly
   textView.setText(formattedText);
}

You can pass these params in your layout file like this:

<TextView
   .
   .
   app:mainText = "@{ priceText }"
   app:priceToFormat = "@{ price }"/>

Good luck.

rbd_sqrl
  • 420
  • 4
  • 14
  • Liked your solutions. Though we are still manipulating in code. Invoking `format(view, sdfgsdfg, 12)`. Your solutions is simply invoking a method which does have annotation of BindingAdapter but that is useless in here. As we are still using TexView.setText. I really want to avoid passing textview and manipulating it in code. – Vikalp Patel Aug 13 '18 at 12:59
  • My end Goal is to take full advantage of DataBinding and avoid all the manipulation in code. – Vikalp Patel Aug 13 '18 at 13:00
  • @VikalpPatel Unless the view has a method defined in with this particular functionality, you will have to manipulate it in your code. Data binding merely binds your data with your views. It doesn't format the data unless you define something like a binding adapter or pass the data to another method to process as far as I know. Good luck. – rbd_sqrl Aug 13 '18 at 13:08
  • @rbd_sqrl : Yes, I know DataBinding merely binds data with views. But my idea to explore the DataBinding to fullest. Yes, we can use Spannable. Thanks for providing solution :) – Vikalp Patel Aug 13 '18 at 13:12
0

use BindingAdapter as mentioned below

    @BindingAdapter("setBold")
    @JvmStatic
    public static void setBold(TextView view, boolean isBold) {
        if (isBold) {
            view.setTypeface(null, Typeface.BOLD);
        } else {
            view.setTypeface(null, Typeface.NORMAL);
        }
    }

And use it in xml as below :

  <androidx.appcompat.widget.AppCompatTextView 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:text="@{model.value}"
        app:setBold="@{model.isBold}"
   />
mani
  • 709
  • 6
  • 13
0

As Html.fromHtml signagure changed at API level 24 (Android N), the Android team introduced HtmlCompat for using the same signature within any api level.

So, you should use the HtmlCompat class:

HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY);

To use it, you may include AndroidX core in your project's build.gradle:

implementation 'androidx.core:core:1.3.1'

Layout XML file:

<?xml version="1.0" encoding="utf-8"?>
 <layout
   <data>
   <import type="androidx.core.text.HtmlCompat"/>
   <data>
     <LinearLayout>
       <android.support.design.widget.CoordinatorLayout>
       <TextView
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:layout_marginEnd="@dimen/spacing_xlarge"
          android:layout_marginStart="@dimen/spacing_xlarge"
          android:layout_marginBottom="@dimen/spacing_small"
          android:text="@{HtmlCompat.fromHtml(@string/product_price(productPrice),HtmlCompat.FROM_HTML_MODE_LEGACY)}"
          android:textColor="@color/button_tertiary"
          android:visibility="@{productPrice > 0}"
          style="@style/Body.Small"
          />
       </android.support.design.widget.CoordinatorLayout>
     </LinearLayout>
 </layout>
-2

// put this method in your model class where name is string variable which is set as per given api response

public Spanned getHtmlText(){
        return Html.fromHtml("<b>" + name + "</b>");
    }

// in xml use this where userList is variable name of model class.

 android:text="@{userList.htmlText}"
Dhara Jani
  • 461
  • 3
  • 10