Spanned as from Html.fromHtml, but with custom ClickableSpan for custom scheme
android, html, spanned, textview
Solution
Option 1 is probably most straightforward and most of the hard work for it has already been done before. You've got the general idea correct: after the HTML has been processed, you can request all the generated `URLSpan` instances and loop through them. You can then replace it with a customized clickable span to get full controls over any of the span clicks.
In the example below, I'm just replacing every `URLSpan` with a simple extension of that class that takes the original url (actually, I should probably say 'uri') and replace its scheme part. I've left the actual `onClick()` logic unimplemented, but I'll leave that up to your imagination.
SpannableStringBuilder builder = ...
URLSpan[] spans = builder .getSpans(0, builder .length(), URLSpan.class);
for (URLSpan span : spans) {
int start = builder .getSpanStart(span);
int end = builder .getSpanEnd(span);
s.removeSpan(span);
span = new CustomURLSpan(span.getURL().replace("http://", "scheme://"));
s.setSpan(span, start, end, 0);
}
textView.setText(builder);
As mentioned earlier, here the `CustomURLSpan` class is a simple extension of `URLSpan` that takes a url and overrides the `onClick()` method so our own logic can be executed there.
public class CustomURLSpan extends URLSpan {
public CustomURLSpan(String url) {
super(url);
}
@Override public void onClick(View widget) {
// custom on click behaviour here
}
}
Some related Q&A's that basically do a similar thing (might be helpful for some more inspiration):
- Can I change TextView link text after using Linkify?
- Remove underline from links in TextView - Android
Problem
I have a TextView that displays some HTML code (images included, ImageGetter). The html is not mine, but I can ask them to include custom scheme links, maybe even tags. Purpose: to display some dynamically generated content without the need to play with nested Android layouts. Problem: some links must be handled in the application (new Fragment loaded). Can't use a receiver for action.VIEW, since it's an Activity Intent, not a broadcast, and its use will be very contextual, so only a programmatically registered receiver would do. I'm using`textView.setText(Html.fromHtml(content.content, imageGetter, null)`. need everything to remain the same, except some spans should have my own `onClick` on it. I'm not very familiar with Spanned, so I see these options: - Edit the SpannableStringBuilder returned from `Html.fromHtml`, and replace the URLSpans I want with a custom ClickableSpan (how?) - As above, but copy everything to a new builder, exchanging the URLSpans for my own (how? `append` takes a CharSequence only, and I get RelativeSizeSpan, StyleSpan, ImageSpan, URLSpan...) - Create a Spanned manually. I can do it for the custom scheme, but how to duplicate the effect of `Html.fromHtml` (or close enough) for all else? [edit] Thanks to MH. for the info. I had tried that before, but failed. Now that i returned to it, I found i had made an error, passing the wrong item to the 1st argument of setSpan. If anyone's interested, i now use this: ``` public static interface OnSpanClickListener { void onClick(String url); } public static SpannableStringBuilder getSpannable(String source, ImageGetter imageGetter, String scheme, final OnSpanClickListener clickHandler){ SpannableStringBuilder b = (SpannableStringBuilder) Html.fromHtml(source, imageGetter, null); for(URLSpan s : b.getSpans(0, b.length(), URLSpan.class)){ String s_url = s.getURL(); if(s_url.startsWith(scheme+"://")){ URLSpan newSpan = new URLSpan(s_url.substring(scheme.length()+3)){ public void onClick(View view) { clickHandler.onClick(getURL()); } }; b.setSpan(newSpan, b.getSpanStart(s), b.getSpanEnd(s), b.getSpanFlags(s)); b.removeSpan(s); } } return b; } ``` (...) ``` body.setText(getSpannable(content.content, imageGetter, getResources().getString(R.string.poster_scheme), new OnSpanClickListener(){ public void onClick(String url) { // do whatever } })); ```