Android TextView with Clickable Links: how to capture clicks?
Solution 1
Based upon another answer, here's a function setTextViewHTML() which parses the links out of a HTML string and makes them clickable, and then lets you respond to the URL.
protected void makeLinkClickable(SpannableStringBuilder strBuilder, final URLSpan span)
{
int start = strBuilder.getSpanStart(span);
int end = strBuilder.getSpanEnd(span);
int flags = strBuilder.getSpanFlags(span);
ClickableSpan clickable = new ClickableSpan() {
public void onClick(View view) {
// Do something with span.getURL() to handle the link click...
}
};
strBuilder.setSpan(clickable, start, end, flags);
strBuilder.removeSpan(span);
}
protected void setTextViewHTML(TextView text, String html)
{
CharSequence sequence = Html.fromHtml(html);
SpannableStringBuilder strBuilder = new SpannableStringBuilder(sequence);
URLSpan[] urls = strBuilder.getSpans(0, sequence.length(), URLSpan.class);
for(URLSpan span : urls) {
makeLinkClickable(strBuilder, span);
}
text.setText(strBuilder);
text.setMovementMethod(LinkMovementMethod.getInstance());
}
Solution 2
I made an easy extension function in Kotlin to catch url link clicks in a TextView by applying a new callback to URLSpan elements.
strings.xml (example link in text)
<string name="link_string">this is my link: <a href="https://www.google.com/">CLICK</a></string>
Make sure your spanned text is set to the TextView before you call "handleUrlClicks"
textView.text = getString(R.string.link_string)
This is the extension function:
/**
* Searches for all URLSpans in current text replaces them with our own ClickableSpans
* forwards clicks to provided function.
*/
fun TextView.handleUrlClicks(onClicked: ((String) -> Unit)? = null) {
//create span builder and replaces current text with it
text = SpannableStringBuilder.valueOf(text).apply {
//search for all URL spans and replace all spans with our own clickable spans
getSpans(0, length, URLSpan::class.java).forEach {
//add new clickable span at the same position
setSpan(
object : ClickableSpan() {
override fun onClick(widget: View) {
onClicked?.invoke(it.url)
}
},
getSpanStart(it),
getSpanEnd(it),
Spanned.SPAN_INCLUSIVE_EXCLUSIVE
)
//remove old URLSpan
removeSpan(it)
}
}
//make sure movement method is set
movementMethod = LinkMovementMethod.getInstance()
}
This is how I call it:
textView.handleUrlClicks { url ->
Timber.d("click on found span: $url")
}
Solution 3
You've done as follows:
text_view.setMovementMethod(LinkMovementMethod.getInstance());
text_view.setText( Html.fromHtml( str_links ) );
have you tried in reverse order as shown below?
text_view.setText( Html.fromHtml( str_links ) );
text_view.setMovementMethod(LinkMovementMethod.getInstance());
and without:
text_view.setLinksClickable(true);
Solution 4
This can be simply solved by using Spannable String.What you really want to do (Business Requirement) is little bit unclear to me so following code will not give exact answer to your situation but i am petty sure that it will give you some idea and you will be able to solve your problem based on the following code.
As you do, i'm also getting some data via HTTP response and i have added some additional underlined text in my case "more" and this underlined text will open the web browser on click event.Hope this will help you.
TextView decription = (TextView)convertView.findViewById(R.id.library_rss_expan_chaild_des_textView);
String dec=d.get_description()+"<a href='"+d.get_link()+"'><u>more</u></a>";
CharSequence sequence = Html.fromHtml(dec);
SpannableStringBuilder strBuilder = new SpannableStringBuilder(sequence);
UnderlineSpan[] underlines = strBuilder.getSpans(0, 10, UnderlineSpan.class);
for(UnderlineSpan span : underlines) {
int start = strBuilder.getSpanStart(span);
int end = strBuilder.getSpanEnd(span);
int flags = strBuilder.getSpanFlags(span);
ClickableSpan myActivityLauncher = new ClickableSpan() {
public void onClick(View view) {
Log.e(TAG, "on click");
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(d.get_link()));
mContext.startActivity(intent);
}
};
strBuilder.setSpan(myActivityLauncher, start, end, flags);
}
decription.setText(strBuilder);
decription.setLinksClickable(true);
decription.setMovementMethod(LinkMovementMethod.getInstance());
Solution 5
I've had the same problem but a lot of text mixed with few links and emails. I think using 'autoLink' is a easier and cleaner way to do it:
text_view.setText( Html.fromHtml( str_links ) );
text_view.setLinksClickable(true);
text_view.setAutoLinkMask(Linkify.ALL); //to open links
You can set Linkify.EMAIL_ADDRESSES or Linkify.WEB_URLS if there's only one of them you want to use or set from the XML layout
android:linksClickable="true"
android:autoLink="web|email"
The available options are: none, web, email, phone, map, all
Comments
-
Zane Claes almost 2 years
I have a TextView which is rendering basic HTML, containing 2+ links. I need to capture clicks on the links and open the links -- in my own internal WebView (not in the default browser.)
The most common method to handle link rendering seems to be like this:
String str_links = "<a href='http://google.com'>Google</a><br /><a href='http://facebook.com'>Facebook</a>"; text_view.setLinksClickable(true); text_view.setMovementMethod(LinkMovementMethod.getInstance()); text_view.setText( Html.fromHtml( str_links ) );
However, this causes the links to open in the default internal web browser (showing the "Complete Action Using..." dialog).
I tried implementing a onClickListener, which properly gets triggered when the link is clicked, but I don't know how to determine WHICH link was clicked...
text_view.setOnClickListener(new OnClickListener(){ public void onClick(View v) { // what now...? } });
Alternatively, I tried creating a custom LinkMovementMethod class and implementing onTouchEvent...
public boolean onTouchEvent(TextView widget, Spannable text, MotionEvent event) { String url = text.toString(); // this doesn't work because the text is not necessarily a URL, or even a single link... // eg, I don't know how to extract the clicked link from the greater paragraph of text return false; }
Ideas?
Example solution
I came up with a solution which parses the links out of a HTML string and makes them clickable, and then lets you respond to the URL.
-
Renjith over 11 yearsWhy dont you use Spannable String.??
-
Zane Claes over 11 yearsIn reality, the HTML is provided by a remote server, not generated by my application.
-
Jonik over 10 yearsYour example solution is very helpful; using that approach I capture clicks nicely and can launch another Activity, with parameters, depending on which link was clicked. (Key point to understand was "Do something with
span.getURL()
".) You could even post it as an answer, as it's better than currently accepted answer!
-
-
Zane Claes over 11 yearsGreat! I've modified this for my case. I'll edit my post to include the code.
-
android developer over 11 yearsis there a similar solution that can be used inside xml ?
-
Jonik over 10 yearsI got it working with OP's modified version (in the question), not with this. (With this version, the clicks went straight to "complete action using" dialog.)
-
Jonik over 10 yearsWorked great. With this approach (unlike the other answers), I managed to 1) capture clicks and 2) launch another Activity, with parameters, depending on which link was clicked.
-
Ray over 10 yearsI used this logic, however had to replace the UnderlineSpan with URLSpan.Also needed to remove the old spans from the SpannableStringBuilder.
-
maverickosama92 almost 10 yearsperfect... saved my day
-
voghDev almost 10 yearsWonderful, but if you apply it to a ListView (i mean, to each element's inner TextView), makes the list unclickable, though links are still clickable
-
Salman Khan over 9 yearsWhats is variable 'd' here ?
-
Sufian over 9 years@voghDev this happens with
ListView
s when aView
'sfocusable
is set true. This usually happens withButton
s/ImageButton
s. Try callingsetFocusable(false)
on yourTextView
. -
Rajath almost 9 yearsMake sure to use
text.setMovementMethod(LinkMovementMethod.getInstance());
if not using URLSpan -
donmezburak almost 9 yearsit removes but does not add new spans :/
-
lightsaber over 8 yearsIt works but without any signal that the user clicked the link, I need a cue/animation/highlight when the link is clicked... what should I do?
-
ademar111190 over 8 years@StarWars you can use (StateList)[developer.android.com/intl/pt-br/guide/topics/resources/… in pure android, but with HTML I don't know.
-
PH88 over 8 yearsI've wrap these into a simpler API in github.com/bluecabin/Textoo for sharing.
-
Geeky Singh over 8 yearsDidn't work for me when using it in AlertDialog with custom view.
-
Nick about 8 yearsI had to add
setMovementMethod(LinkMovementMethod.getInstance());
in the makeLinkClickable method for this to work. -
X09 about 8 yearsPlease, check your code again, from line
text_view.setMovementMethod(new TextViewClickMovement(this, context));
; Android Studio is complaining thatcontext
could not be resolved. -
Admin about 8 yearsIf you copy source code from bitbucket, you should change place of context and listener like this text_view.setMovementMethod(new TextViewClickMovement( context. this));
-
X09 about 8 yearsThat'll be parsing two context for the parameters. Didn't work Sir. Though the accepted answer is working for me now
-
tir38 over 7 yearsBecause this method replaces
URLSpan
s withClickableSpan
s, you won't be able to use Espresso to click on items (i.e.ViewActions.openLinkWithText
). -
Pär Nils Amsen over 7 yearsFinally something that works, tried a lot of solutions for this.
-
Vulovic Vukasin over 7 yearsThank you for your answer sir, best one out there for this kind, thank you!
-
DmitryKanunnikoff about 7 yearsThank you! Works.
-
jL4 about 6 yearsDid not work until I replaced
SpannableStringBuilder strBuilder = new...
withSpannable strBuilder = (Spannable) sequence;
-
Manmohan Soni almost 6 yearsHi is there any way to intercept the intent fired at the time of click of a link
-
Valentin over 5 yearsYou saved my day. Thank you :)
-
Jordi over 5 yearsIt's been 6 years from this answer.. Of course it may have changed in latest Android version ^^ It doesn't mean it doesn't worked back then ^^
-
SKG almost 5 yearsAwesome sauce! Thank you!
-
Aleksandr Urzhumtcev almost 5 yearsBe aware, when you use
ClickableSpan
instead ofURLSpan
, you loose logic which opens the link in browser. UseURLSpan
or implement this logic by yourself. -
Sumit Kumar over 4 yearsI spent 2 hour on this and finally two functions solve my problem. Thanks a lot and also learn from this code.
-
tm1701 over 4 yearsAbsolutely SUPERB!
-
Neela about 4 yearsIt works for me with the following lines, val strBuilder = SpannableStringBuilder(htmlString) sunset_message.text = strBuilder sunset_message.linksClickable = true sunset_message.movementMethod = LinkMovementMethod.getInstance()
-
Milind Mevada about 4 yearsIs it possible to override a onClick event using this?
-
Valentin Yuryev almost 4 yearsAwesome!_______
-
Damanpreet Singh about 3 yearsNice extension but setting text to the text view directly by getting string is a bit misleading.. You need to set it using textView.text = HtmlCompat.fromHtml(htmlText, HtmlCompat.FROM_HTML_MODE_COMPACT)
-
Niroshan almost 3 yearsthis working only for hyperlinks. If text contain email, phone number then not clickable.
-
artenson.art98 over 2 yearsIf you want to change the highlight color of the link, set a color using
android:textColorHighlight="yourColor"
on yourTextView
-
Anh Duy about 2 yearsThis code works fine even when setting android:textIsSelectable="true" in xml. Thank you very much!
-
Kannan_SJD about 2 yearsGive this man an award!