Nothing earth-shattering in this post, but maybe this will help you if you run into this seemingly trivial problem.

The Problem

You have a string of Html. It probably has all the html tags, e.g, <a />, xml encoded like &lta /&gt;. You want to render this string of Html in a TextView. You also want to know when a link is clicked so you can do something™ with it.

The Solution

Wrap a TextView in a ScrollView

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"  
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/html_rendered_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</ScrollView>  

Reference your TextView in your Android code (here in an Activity)

private TextView textView;

@Override
protected void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    textView = (TextView) findViewById(R.id.html_rendered_text_view);

    renderHtmlInATextView();
}

Then...

  • Load your Html into memory
  • Replace all of the xml encodings with Html
  • Create a SpannableString from a Spanned of the Html
  • Hack - set your TextView with the SpannableString, this is so you can get the actual rendered text in the ClickableSpan
  • Replace all of the ClickableSpan's with your own ClickableSpan so you can override onClick
  • Set the SpannableString on the TextView

Here it is all together

private void renderHtmlInATextView() {  
    textView.setLinksClickable(true);
    textView.setMovementMethod(LinkMovementMethod.getInstance());

    String html = getString(R.string.html);

    //replaces all the < and > signs in the html
    String replaceHtml = html
            .replaceAll("&lt;", "<")
            .replaceAll("&gt;", ">");

    Spanned span = Html.fromHtml(replaceHtml);
    Spannable spannable = new SpannableString(span);

    textView.setText(spannable, TextView.BufferType.SPANNABLE);

    ClickableSpan [] clickSpans = spannable.getSpans(0, span.length(), ClickableSpan.class);

    if (clickSpans != null) {
        for (int i = 0; i < clickSpans.length; i++) {
            ClickableSpan thisClickSpan = clickSpans[i];

            int start = spannable.getSpanStart(thisClickSpan);
            int end = spannable.getSpanEnd(thisClickSpan);
            int flags = spannable.getSpanFlags(thisClickSpan);

            final CharSequence clickText = textView.getText().subSequence(start, end);

            spannable.setSpan(new ClickableSpan() {
                @Override
                public void onClick(View view) {
                    Toast.makeText(MainActivity.this, clickText, Toast.LENGTH_SHORT).show();
                }
            }, start, end, flags);

            spannable.removeSpan(clickSpans[i]);
        }
    }

    textView.setText(spannable, TextView.BufferType.SPANNABLE);
}

So, there you go, the most boring blog post ever, but I hope it helps someone out there.

Have a great day!