153

I have a long text in one of the strings at strings.xml. I want to make bold and change the color of some words in that text.

How can I do it?

Ali Khaki
  • 1,150
  • 1
  • 12
  • 23
Caroline
  • 3,653
  • 5
  • 17
  • 25

12 Answers12

242

You could basically use html tags in your string resource like:

<resource>
    <string name="styled_welcome_message">We are <b><i>so</i></b> glad to see you.</string>
</resources>

And use Html.fromHtml or use spannable, check the link I posted.

Old similar question: Is it possible to have multiple styles inside a TextView?

Community
  • 1
  • 1
David Olsson
  • 7,932
  • 3
  • 27
  • 38
  • 21
    Also, depending how you use your resource string, you may need to put the bold/italic tags in a CDATA block so they don't get parsed until it's used by `Html.fromHtml()`: ... `so]]>` ... – dule Mar 30 '12 at 19:33
  • 164
    For anybody else who found the official Android documentation a bit too vague on this: if you use tags like in your string resource, make sure you retrieve it using `getText(R.string.whatever)` rather than `getString(R.string.whatever)` – andygeers Jun 09 '15 at 12:15
  • 1
    isn't it supposed to be `name` instead of `id`? – Hendra Anggrian Feb 23 '16 at 21:24
  • No Hendra, it should be name not id. – Ishaan Aug 26 '16 at 13:28
  • 7
    So no need for `Html.fromHtml` or `Spannable`. Just use `getText(R.string.whatever)` like @andygeers mentioned. – Alaa M. Nov 30 '16 at 20:17
  • 1
    @andygeers `getText ` from Java file. How to do it If we are using it in layout xml? – Moinkhan Apr 25 '19 at 11:21
  • 4
    @andygeers What can we do for a string like "My name is %s" which cannot use getText as it only accepts a single parameter? – Taylor Jan 22 '20 at 20:21
  • Taylor, you can refer answer of Saga chorage, It worked with place holder text – NguyenDat Mar 26 '20 at 08:21
  • @andygeers saved the day for me – El Sushiboi Sep 23 '20 at 22:19
  • In this answer, tag stands for Bold, and for Italic, so if someone just want bold words only needs to use the first tag – Ariel Jan 11 '21 at 20:18
  • 2
    still, I'm facing an issue, using %1$s between string inside the String.xml file. getString(v1, v2) can't replace with getText(v1, v2) – Muhammed Haris Feb 18 '21 at 08:47
  • 1
    @Taylor we have a solution: %1$s glad to see you.]]> inside class: textView?.setText(HtmlCompat.fromHtml(getString(R.string.styled_welcome_message, "sample"), HtmlCompat.FROM_HTML_MODE_LEGACY), TextView.BufferType.SPANNABLE) – Muhammed Haris Feb 18 '21 at 10:46
57

Use html tag inside string resources :-

<resources>
<string name="string_resource_name"><![CDATA[<b> Your text </b>]]> </string>
</resources>

And get bold text from string resources like :-

private Spanned getSpannedText(String text) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return Html.fromHtml(text, Html.FROM_HTML_MODE_COMPACT);
        } else {
            return Html.fromHtml(text);
        }
    }


 String s = format(context.getResources().getString(R.string.string_resource_name));
 textView.setText(getSpannedText(s));
Sagar Chorage
  • 1,342
  • 11
  • 12
47

As David Olsson has said, you can use HTML in your string resources:

<resource>
    <string name="my_string">A string with <i>actual</i> <b>formatting</b>!</string>
</resources>

Then if you use getText(R.string.my_string) rather than getString(R.string.my_string) you get back a CharSequence rather than a String that contains the formatting embedded.

andygeers
  • 6,677
  • 9
  • 47
  • 63
11

In kotlin, you can create extensions functions on resources (activities|fragments |context) that will convert your string to an html span

e.g.

fun Resources.getHtmlSpannedString(@StringRes id: Int): Spanned = getString(id).toHtmlSpan()

fun Resources.getHtmlSpannedString(@StringRes id: Int, vararg formatArgs: Any): Spanned = getString(id, *formatArgs).toHtmlSpan()

fun Resources.getQuantityHtmlSpannedString(@PluralsRes id: Int, quantity: Int): Spanned = getQuantityString(id, quantity).toHtmlSpan()

fun Resources.getQuantityHtmlSpannedString(@PluralsRes id: Int, quantity: Int, vararg formatArgs: Any): Spanned = getQuantityString(id, quantity, *formatArgs).toHtmlSpan()

fun String.toHtmlSpan(): Spanned = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    Html.fromHtml(this, Html.FROM_HTML_MODE_LEGACY)
} else {
    Html.fromHtml(this)
}

Usage

//your strings.xml
<string name="greeting"><![CDATA[<b>Hello %s!</b><br>]]>This is newline</string>

//in your fragment or activity
resources.getHtmlSpannedString(R.string.greeting, "World")

EDIT even more extensions

fun Context.getHtmlSpannedString(@StringRes id: Int): Spanned = getString(id).toHtmlSpan()

fun Context.getHtmlSpannedString(@StringRes id: Int, vararg formatArgs: Any): Spanned = getString(id, *formatArgs).toHtmlSpan()

fun Context.getQuantityHtmlSpannedString(@PluralsRes id: Int, quantity: Int): Spanned = resources.getQuantityString(id, quantity).toHtmlSpan()

fun Context.getQuantityHtmlSpannedString(@PluralsRes id: Int, quantity: Int, vararg formatArgs: Any): Spanned = resources.getQuantityString(id, quantity, *formatArgs).toHtmlSpan()


fun Activity.getHtmlSpannedString(@StringRes id: Int): Spanned = getString(id).toHtmlSpan()

fun Activity.getHtmlSpannedString(@StringRes id: Int, vararg formatArgs: Any): Spanned = getString(id, *formatArgs).toHtmlSpan()

fun Activity.getQuantityHtmlSpannedString(@PluralsRes id: Int, quantity: Int): Spanned = resources.getQuantityString(id, quantity).toHtmlSpan()

fun Activity.getQuantityHtmlSpannedString(@PluralsRes id: Int, quantity: Int, vararg formatArgs: Any): Spanned = resources.getQuantityString(id, quantity, *formatArgs).toHtmlSpan()


fun Fragment.getHtmlSpannedString(@StringRes id: Int): Spanned = getString(id).toHtmlSpan()

fun Fragment.getHtmlSpannedString(@StringRes id: Int, vararg formatArgs: Any): Spanned = getString(id, *formatArgs).toHtmlSpan()

fun Fragment.getQuantityHtmlSpannedString(@PluralsRes id: Int, quantity: Int): Spanned = resources.getQuantityString(id, quantity).toHtmlSpan()

fun Fragment.getQuantityHtmlSpannedString(@PluralsRes id: Int, quantity: Int, vararg formatArgs: Any): Spanned = resources.getQuantityString(id, quantity, *formatArgs).toHtmlSpan()
svkaka
  • 3,682
  • 29
  • 50
6

Strings.xml

<string name="my_text"><Data><![CDATA[<b>Your text</b>]]></Data></string>

To set

textView.setText(Html.fromHtml(getString(R.string.activity_completed_text)));
kunal khedkar
  • 789
  • 7
  • 5
1

You can do it from string

 <resources xmlns:tools="http://schemas.android.com/tools">

 <string name="total_review"><b>Total Review: </b> </string>

 </resources>

and can access it from the java code like

proDuctReviewNumber.setText(getResources().getString(R.string.total_review)+productDetailsSuccess.getProductTotalReview());
pavel
  • 1,408
  • 17
  • 17
1

In Kotlin I have created an extension function for the Context. It takes a @StringRes and optionally you can provide parameters as well.

fun Context.fromHtmlWithParams(@StringRes stringRes: Int, parameter : String? = null) : Spanned {

    val stringText = if (parameter.isNullOrEmpty()) {
                    this.getString(stringRes)
                } else {
                    this.getString(stringRes, parameter)
                }

    return Html.fromHtml(stringText, Html.FROM_HTML_MODE_LEGACY)

}

Usage

tv_directors.text = context?.fromHtmlWithParams(R.string.directors, movie.Director)
inspire_coding
  • 298
  • 7
  • 11
1

here it's the solution for if there have any assigned values inside the string.xml file.

 <string name="styled_welcome_message"><![CDATA[We are <b> %1$s </b>  glad to see you.]]></string>

set in to TextView:

textView?.setText(HtmlCompat.fromHtml(getString(R.string.styled_welcome_message, "sample"), HtmlCompat.FROM_HTML_MODE_LEGACY), TextView.BufferType.SPANNABLE)

PS: don't forget to wrap your text (in String Resources) within:

<![CDATA[ YOUR TEXT HERE ]]>
Muhammed Haris
  • 312
  • 4
  • 14
0

strings.xml

<string name="sentence">This price is <b>%1$s</b> USD</string>

page.java

String successMessage = getText(R.string.message,"5.21");

This price 5.21 USD

Jemshit Iskenderov
  • 8,415
  • 5
  • 60
  • 98
Samet ÖZTOPRAK
  • 2,734
  • 3
  • 27
  • 31
  • 1
    You should use native functionality for passing parameters. Just place "%1$s" instead of "{1}", and call getString(R.string.message, "5.21") without replace() – Adrian Grygutis Apr 01 '20 at 07:09
  • 2
    The docs support this answer: https://developer.android.com/guide/topics/resources/string-resource#StylingWithHTML Though it does need to be passed through an HTML parser – ProjectDelta Jun 17 '20 at 10:46
  • 2
    This answer is not correct. (or at least the last edit). The method getText(int) takes only one parameter - the string resource id. @Jemshit Iskenderov please check your edit – Alex Busuioc Feb 24 '21 at 17:45
  • 1
    @AlexBusuioc ik, either way its incorrect, i downvoted. If u use getString, bold format is gone. If you use getText, argument is gone – Jemshit Iskenderov Feb 24 '21 at 17:54
  • getText doesn't support additional values – Hamza Khan Apr 01 '21 at 10:10
0

I was having a text something like:

Forgot Password? Reset here.

To implement this the easy way I used the existing android:textStyle="bold"

<LinearLayout
        android:id="@+id/forgotPassword"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center"
        >


        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:autoLink="all"
            android:linksClickable="false"
            android:selectAllOnFocus="false"

            android:text="Forgot password? "
            android:textAlignment="center"
            android:textColor="@android:color/white"
            />

        <TextView

            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:autoLink="all"
            android:linksClickable="false"
            android:selectAllOnFocus="false"

            android:text="Reset here"
            android:textAlignment="center"
            android:textColor="@android:color/white"
            android:textStyle="bold" />
    </LinearLayout>

Maybe it helps someone

0

If you want to store formatted as a string variable and reuse it to text view isn't possible

If you aren't applying formatting, you can set TextView text directly by calling setText(java.lang.CharSequence). In some cases, however, you may want to create a styled text resource that is also used as a format string. Normally, this doesn't work because the format(String, Object...) and getString(int, Object...) methods strip all the style information from the string. The work-around to this is to write the HTML tags with escaped entities, which are then recovered with fromHtml(String), after the formatting takes place.

by from docs so alternatively store the string id and everyttime bind get string from using Htlm.fromHtml().. method.

ex:

Kotlin extention

fun Context.getStringFromResource(stringId: Int): Spanned {
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        Html.fromHtml(java.lang.String.format(resources.getString(stringId)), 
 Html.FROM_HTML_MODE_COMPACT)
    } else {
        Html.fromHtml(java.lang.String.format(resources.getString(stringId)))
    }
}

In code:

getStringFromResource(id)
Arul
  • 643
  • 7
  • 21
0

If you want to use basic HTML tags in a string resource, it is necessary to escape the tags, otherwise they are ignored. Simply, replace '<' with &lt; and '>' with '&gt;' e.g:

<string name="bold_statement">This is a &lt;bold&gt; statement</string>

Will produce: This is a bold statement. When loading the String resource, make sure to use

HtmlCompat.fromHtml(stringFromResource, HtmlCompat.FROM_HTML_MODE_COMPACT)