163

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,184
  • 1
  • 13
  • 24
Caroline
  • 3,763
  • 5
  • 19
  • 25

15 Answers15

254

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
  • 8,085
  • 3
  • 30
  • 38
  • 22
    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()`: ... `<![CDATA[so]]>` ... – dule Mar 30 '12 at 19:33
  • 174
    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
  • 9
    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
  • 6
    @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
  • 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
  • 3
    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: <![CDATA[We are %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
61

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,460
  • 12
  • 13
50

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,909
  • 9
  • 49
  • 63
13

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,942
  • 2
  • 31
  • 55
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
  • 877
  • 8
  • 6
5

Simple and Latest answer from the official Android site: source

First, you will add the string in your strings.xml like this:

<string name="welcome">Welcome to <b>Android</b>!</string>

Here you will replace the < before the b with &lt;

To be like this:

<string name="welcome">Welcome to &lt;b>Android&lt;/b>!</string>

In kotlin code you will make an extension to parse strings from html:

fun String.fromHtml(): Spanned {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    // FROM_HTML_MODE_LEGACY is the behaviour that was used for versions below android N
    // we are using this flag to give a consistent behaviour
    Html.fromHtml(this, Html.FROM_HTML_MODE_LEGACY)
} else {
    Html.fromHtml(this)
 }
}

Then use it like this:

getString(R.string.welcome).fromHtml()
Mahmoud Ayman
  • 1,157
  • 1
  • 17
  • 27
  • this should now be the correct answer, only note that there is HtmlCompat class and no need anymore extended-function 'fromHtml' – Xenione Jan 10 '23 at 11:11
3

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 ]]>
Yasser AKBBACH
  • 539
  • 5
  • 7
Muhammed Haris
  • 340
  • 1
  • 5
  • 15
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,603
  • 22
  • 19
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)
PeterPazmandi
  • 533
  • 10
  • 13
1

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
  • 1,031
  • 13
  • 23
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
  • 9,501
  • 5
  • 69
  • 106
Samet ÖZTOPRAK
  • 3,112
  • 3
  • 32
  • 33
  • 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
  • 3
    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
  • 2
    @AlexBusuioc ik, either way its incorrect, i downvoted. If u use getString, bold format is gone. If you use getText, argument is gone – Jemshit 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 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)
0

Imagine you want nicely formatted string like this:

enter image description here

I do this:

    <string name="dialog_upgrade_item_fx_output_text">
 "[B]Unlimited support for up to 5 audio effects per audio track[/B]\n
 - Multiple high quality effects available Reverb, Delay, Distortion, Compressor, Equalizer\n
 - Each comes with various presets or create your own"
    </string>

fun Context.formatted(resId: Int): Spanned =
    Html.fromHtml(getString(resId).replace("\n", "<br>")
        .replace("[B]", "<b>").replace("[/B]", "</b>"), 0)

textView(R.id.dialog_upgrade_item_text)
            .text(formatted(R.string.dialog_upgrade_item_fx_output_text))
Renetik
  • 5,887
  • 1
  • 47
  • 66
0

There is also native support for the same, where we don't have to hardcode any string or index and it plays way better in internalisation of the strings

You can define the strings like below in strings.xml as that will help in the translation of the string in other languages as well

<string name="styled_hello_world"><annotation type="bold">bold text</annotation> Hello World</string>

You can apply string id in your TextView in your UI XML.

And in the code, you can write something like below

val spannedText = textView.text as SpannedString
// get all the annotation spans from the text
val annotations = spannedText.getSpans(0, spannedText.length, android.text.Annotation::class.java)
val spannableString = SpannableString(spannedText)
annotations.forEach { annotation ->
    if (annotation.key == "type") {
        val styleValue = annotation.value
        if (styleValue == "bold") {
            spannableString.setSpan(
                StyleSpan(android.graphics.Typeface.BOLD),
                spannedText.getSpanStart(annotation),
                spannedText.getSpanEnd(annotation),
                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
            )
        }
    }
}
textView.text = spannableString

You can find more details in the blog post

AndroidEngineX
  • 975
  • 1
  • 9
  • 22