Set TextView text from html-formatted string resource in XML
Asked Answered
H

8

208

I have some fixed strings inside my strings.xml, something like:

<resources>
    <string name="somestring">
        <B>Title</B><BR/>
        Content
    </string>
</resources>

and in my layout I've got a TextView which I'd like to fill with the html-formatted string.

<TextView android:id="@+id/formattedtext"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/htmlstring"/>

if I do this, the content of formattedtext is just the content of somestring stripped of any html tags and thus unformatted.

I know that it is possible to set the formatted text programmatically with

.setText(Html.fromHtml(somestring));

because I use this in other parts of my program where it is working as expected.

To call this function I need an Activity, but at the moment my layout is just a simple more or less static view in plain XML and I'd prefer to leave it that way, to save me from the overhead of creating an Activity just to set some text.

Am I overlooking something obvious? Is it not possible at all? Any help or workarounds welcome!

Edit: Just tried some things and it seems that HTML formatting in xml has some restraints:

  • tags must be written lowercase

  • some tags which are mentioned here do not work, e.g. <br/> (it's possible to use \n instead)

Haeckel answered 13/7, 2010 at 7:51 Comment(3)
Long time now I know, but I was able to use <br> and not \n for a new line using the html for a TextView.Antinomy
Can this work from just xml? I have a small bounty out on the answer. I'd take no as a factual answers at this point I've already coded around it.Vange
I find it interesting that no one actually answered the question. The OP states he knows how to do it with fromHtml(), but wants to do it directly in the layout file (although, the reasons aren't good... you always have an activity/context available if you're drawing on the screen). None of the answers dive into the effects of anchor tags, either.Darelldarelle
D
504

Just in case anybody finds this, there's a nicer alternative that's not documented (I tripped over it after searching for hours, and finally found it in the bug list for the Android SDK itself). You CAN include raw HTML in strings.xml, as long as you wrap it in

<![CDATA[ ...raw html... ]]>

Edge Cases:

  • Characters like apostrophe ('), double-quote ("), and ampersand (&) only need to be escaped if you want them to appear in the rendered text AS themselves, but they COULD be plausibly interpreted as HTML.
    • ' and " can be represented as\' and \", or &apos; and &quot;.
    • < and > always need to be escaped as &lt; and &gt; if you literally want them to render as '<' and '>' in the text.
    • Ampersand (&) is a little more complicated.
      • Ampersand followed by whitespace renders as ampersand.
      • Ampersand followed by one or more characters that don't form a valid HTML entity code render as Ampersand followed by those characters. So... &qqq; renders as &qqq;, but &lt;1 renders as <1.

Example:

<string name="nice_html">
<![CDATA[
<p>This is a html-formatted \"string\" with <b>bold</b> and <i>italic</i> text</p>
<p>This is another paragraph from the same \'string\'.</p>
<p>To be clear, 0 &lt; 1, & 10 &gt; 1<p>
]]>
</string>

Then, in your code:

TextView foo = (TextView)findViewById(R.id.foo);
foo.setText(Html.fromHtml(getString(R.string.nice_html), FROM_HTML_MODE_LEGACY));

IMHO, this is several orders of magnitude nicer to work with :-)


August 2021 update: My original answer used Html.fromHtml(String), which was deprecated in API 24. The alternative fromHtml(String,int) form is suggested as its replacement.

FROM_HTML_MODE_LEGACY is likely to work... but one of the other flags might be a better choice for what you want to do.

On a final note, if you'd prefer to render Android Spanned text suitable for use in a TextView using Markdown syntax instead of HTML, there are now multiple thirdparty libraries to make it easy including https://noties.io/Markwon.

Drunken answered 28/5, 2011 at 3:52 Comment(15)
Just to add, you can also backslash-escape apostrophes/single-quotes inside the CDATA block, so you can have things like <b>can\'t</b> instead of the infinitely-uglier <b>can&apos;t</b>Drunken
Does this still work? I just tried it and it literally displays the <p> and </p>.Westphal
@PeriHartman Did you also do the Html.fromHtml(getString(R.string.nice_html)) bit? :)Deitz
Where does TextView foo = (TextView)findViewById(R.id.foo); foo.setText(Html.fromHtml(getString(R.string.nice_html))); need to go?Singlecross
Is there a way to avoid "Html.fromHtml" ?Pokeberry
anyway to do in it XML directlyCoenurus
\n not producing the new lineLeucoma
@PrashanthDebbadwar In HTML you can create a linebreak using the <br> tag, eg. <p>This is line one.<br>This is line two.</p>. Note that it doesn't need to be closed.Limerick
@MushtaqJameel Just add your HTML string resource in strings file and set it directly in your xml layout's textview using @string/<resource_identifier> . It will automatically render as HTML text, no need of Java code.Jimerson
you sir. just saved me.Erigena
This produces error "Error:(738) Multiple substitutions specified in non-positional format; did you mean to add the formatted="false" attribute?" while compiling. What can I do?Passade
This needs an update for Android N. New Html.fromHtml method introducedRelive
You forgot the ! at the end of the CD tagLashay
@PrashanthDebbadwar what did you do for that?Glossology
fromHtml(String) is deprecated. Google has created HtmlCompat which can be used instead of the method below. Add this dependency implementation 'androidx.core:core:1.0.1 to the build.gradle file of your app. Make sure you use the latest version of androidx.core:core. --> #37905239Genotype
C
141

As the top answer here is suggesting something wrong (or at least too complicated), I feel this should be updated, although the question is quite old:

When using String resources in Android, you just have to call getString(...) from Java code or use android:text="@string/..." in your layout XML.

Even if you want to use HTML markup in your Strings, you don't have to change a lot:

The only characters that you need to escape in your String resources are:

  • double quotation mark: " becomes \"
  • single quotation mark: ' becomes \'
  • ampersand: & becomes &#38; or &amp;

That means you can add your HTML markup without escaping the tags:

<string name="my_string"><b>Hello World!</b> This is an example.</string>

However, to be sure, you should only use <b>, <i> and <u> as they are listed in the documentation.

If you want to use your HTML strings from XML, just keep on using android:text="@string/...", it will work fine.

The only difference is that, if you want to use your HTML strings from Java code, you have to use getText(...) instead of getString(...) now, as the former keeps the style and the latter will just strip it off.

It's as easy as that. No CDATA, no Html.fromHtml(...).

You will only need Html.fromHtml(...) if you did encode your special characters in HTML markup. Use it with getString(...) then. This can be necessary if you want to pass the String to String.format(...).

This is all described in the docs as well.

Edit:

There is no difference between getText(...) with unescaped HTML (as I've proposed) or CDATA sections and Html.fromHtml(...).

See the following graphic for a comparison:

enter image description here

Cassaundracassava answered 13/8, 2013 at 2:4 Comment(14)
Mine is stripping it off. I am using the following code: getResources().getText(R.string.codename)Wirehaired
Please provide a bit more context: What is the content of the codename string resource? It should include unescaped HTML tags. And where are you using the result of getText(...) then?Cassaundracassava
yes, actually It does not work fom me too. It strips away the <a> html tagBusinesslike
In order to help better, I'd need to see the Java/XML excerpts. But as I've written: You are only sure if you're using <b>, <i>, <u>. Other tags are not always supported.Cassaundracassava
Your answer works and is simple. Just put your string in strings.xml, with the desired html tags, and reference it from your layout xml file. I used the <sup> tag.Westphal
Yours is another solution, not an alternative to the other because it doesn't support all tags, like the other does. I fail to see any advantage in this solution when the other is very simple.Prothalamion
@androidpotato7 Don't know what you're referring to. This answer supports the same tags that the others support as well. Support for tags is done by the operating system itself and not affected by the way you put your HTML into your strings.Cassaundracassava
As I experienced using "CDATA" have a problem, something like this for example: <![CDATA[...HTML...]]> that have 22 letters and html part have 10. so if you use HTML.fromhtml(getstring(...)) your text will be show correctly but text's content need 22 letters spaces. I used in listview and seen weedy spaces. so gettext() is BETTER.Kaufman
Thank you a lot . ... your answer helped me a lott... :)Paleogeography
Does that still work? <string name="prospects_nearing_limit"><![CDATA[<b>Blah blah should be bolded</b> Blah blah shouldn't be bolded]]></string> and <TextView .... android:text="@string/prospects_nearing_limit" /> butSuez
For anyone else having issues using this solution, try using getText but don't call toString and just pass the result to setText. That managed to retain the tags for me.Altostratus
@MichaelKiros Of course! Sorry, that should have been made clearer in the answer. The methods setText and getText support CharSequence instances like Spanned, specifically Spannable, which can contain more information than just plain text. So you should avoid casting to plain String.Cassaundracassava
Unfortunately the getText() method has another limitation: it does not allow parameter substitution, unlike getString(resource, param1, param2...). Using getString() with the CDATA in the resource xml works fine in this case.Morita
Does this also work for an <a href? can't find it in the docs, but it does work for some reason in my app, but could their be any issues using it?Legerdemain
H
16

Escape your HTML tags ...

<resources>
    <string name="somestring">
        &lt;B&gt;Title&lt;/B&gt;&lt;BR/&gt;
        Content
    </string>
</resources>
Hipparchus answered 13/7, 2010 at 13:7 Comment(5)
That is what the docs say: developer.android.com/intl/zh-TW/guide/topics/resources/… but is that enough for the TextView to show HTML?Cholla
I have tested this, and if you use it from the java source code, it's fine. But if you associate your text directly from layout XML, the tags are shown in the text view (e.g. <B>Title</B> ... )Chilopod
To show the text in a view, use the class [developer.android.com/reference/android/text/Html.html], specifically fromHTML().Hipparchus
@Chilopod is right, using android:text="" from a layout with the HTML will show the tags in the string.Fuddle
If you want to use the HTML string directly from XML, you may not encode HTML special chars in the string. However, if you want to use the string from code via Html.fromHTML() you have to encode the special chars. Strange!Cassaundracassava
A
13

Android does not have a specification to indicate the type of resource string (e.g. text/plain or text/html). There is a workaround, however, that will allow the developer to specify this within the XML file.

  1. Define a custom attribute to specify that the android:text attribute is html.
  2. Use a subclassed TextView.

Once you define these, you can express yourself with HTML in xml files without ever having to call setText(Html.fromHtml(...)) again. I'm rather surprised that this approach is not part of the API.

This solution works to the degree that the Android studio simulator will display the text as rendered HTML.

enter image description here

res/values/strings.xml (the string resource as HTML)

<resources>
<string name="app_name">TextViewEx</string>
<string name="string_with_html"><![CDATA[
       <em>Hello</em> <strong>World</strong>!
 ]]></string>
</resources>

layout.xml (only the relevant parts)

Declare the custom attribute namespace, and add the android_ex:isHtml attribute. Also use the subclass of TextView.

<RelativeLayout
...
xmlns:android_ex="http://schemas.android.com/apk/res-auto"
...>

<tv.twelvetone.samples.textviewex.TextViewEx
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/string_with_html"
    android_ex:isHtml="true"
    />
 </RelativeLayout>

res/values/attrs.xml (define the custom attributes for the subclass)

 <resources>
<declare-styleable name="TextViewEx">
    <attr name="isHtml" format="boolean"/>
    <attr name="android:text" />
</declare-styleable>
</resources>

TextViewEx.java (the subclass of TextView)

 package tv.twelvetone.samples.textviewex;

 import android.content.Context;
 import android.content.res.TypedArray;
 import android.support.annotation.Nullable;
 import android.text.Html;
 import android.util.AttributeSet;
 import android.widget.TextView;

public TextViewEx(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TextViewEx, 0, 0);
    try {
        boolean isHtml = a.getBoolean(R.styleable.TextViewEx_isHtml, false);
        if (isHtml) {
            String text = a.getString(R.styleable.TextViewEx_android_text);
            if (text != null) {
                setText(Html.fromHtml(text));
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        a.recycle();
    }
}
}
Aerialist answered 22/9, 2016 at 3:9 Comment(1)
This approach will be better, If app uses HTML formatted strings in multiple places rather calling HTML.fromHtml every place. App itself will have two views one for normal and one for HTML.Simian
W
11

Latest update:

Html.fromHtml(string);//deprecated after Android N versions..

Following code give support to android N and above versions...

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
textView.setText(Html.fromHtml(yourHtmlString,Html.FROM_HTML_MODE_LEGACY));
}

else 
{
textView.setText(Html.fromHtml(yourHtmlString));
}
Wain answered 22/11, 2016 at 14:51 Comment(0)
K
5
  String termsOfCondition="<font color=#cc0029>Terms of Use </font>";
  String commma="<font color=#000000>, </font>";
  String privacyPolicy="<font color=#cc0029>Privacy Policy </font>";
  Spanned text=Html.fromHtml("I am of legal age and I have read, understood, agreed and accepted the "+termsOfCondition+commma+privacyPolicy);
        secondCheckBox.setText(text);
Kildare answered 24/1, 2019 at 11:12 Comment(0)
M
2

I have another case when I have no chance to put CDATA into the xml as I receive the string HTML from a server.

Here is what I get from a server:

<p>The quick brown&nbsp;<br />
fox jumps&nbsp;<br />
 over the lazy dog<br />
</p>

It seems to be more complicated but the solution is much simpler.

private TextView textView;

protected void onCreate(Bundle savedInstanceState) { 
.....
textView = (TextView) findViewById(R.id.text); //need to define in your layout
String htmlFromServer = getHTMLContentFromAServer(); 
textView.setText(Html.fromHtml(htmlFromServer).toString());

}

Hope it helps!
Linh

Mongol answered 11/3, 2014 at 3:1 Comment(1)
where is the implementation of getHTMLContentFromAServer() functionMiscellanea
C
0

If you want to show html scrip in android app Like TextView

Please follow this code

Kotlin

var stringvalue = "Your Sting"

yourTextVew.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            Html.fromHtml(stringvalue, Html.FROM_HTML_MODE_COMPACT)
        } else {
            Html.fromHtml(stringvalue)
        }

Java

String stringvalue = "Your String";

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                   yourTextVew.setText(Html.fromHtml(stringvalue, Html.FROM_HTML_MODE_COMPACT))
                } else {
                   yourTextVew.setText( Html.fromHtml(stringvalue))
                }
Churchyard answered 9/3, 2022 at 6:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.