There are two ways of “linkifying” URLs in a TextView. First, as an XML attribute:
<TextView
...
android:autoLink="phone|web" />
and second, programmatically:
TextView textView = (TextView) findViewById(R.id.text1);
Linkify.addLinks(textView, Linkify.PHONE_NUMBERS | LINKIFY.WEB_URLS);
In both the cases, the framework internally registers a LinkMovementMethod
on the TextView that handles dispatching a ACTION_VIEW
Intent when any link is clicked. This is why phone-numbers open in a dialer when clicked, web URLs open in a browser, map URLs open in Google Maps and so on. The source can be seen in URLSpan.class (line #63):
@Override
public void onClick(View widget) {
Uri uri = Uri.parse(getURL());
Context context = widget.getContext();
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
try {
context.startActivity(intent);
}
...
}
The problem with the default LinkMovementMethod
is that it’s buggy and non-customizable:
1. Incorrect touch areas
It incorrectly calculates a URL’s bounds when the URL is present at any horizontal/vertical end and there’s space available in that direction inside the TextView
(including its padding). This is like having ghost links in our app and that’s not good.
2. Unreliable highlighting
LinkMovementMethod
highlights a URL only the first time it’s clicked and it stops working randomly after that. It also does not correctly track the touch pointer to unhighlight the URL once it’s no longer being touched.
3. No support for custom URL handling
We’re also out of luck if we want to show contextual options when a phone-number is clicked instead of simply redirecting to the dialer.
BetterLinkMovementMethod
Introducing BetterLinkMovementMethod
, a.. uh.. better a version of LinkMovementMethod
that solves all our problems. It’s designed to be a drop-in replacement for LinkMovementMethod:
TextView textView = (TextView) findViewById(R.id.text1);
textView.setMovementMethod(BetterLinkMovementMethod.newInstance());
Linkify.addLinks(textView, Linkify.PHONE_NUMBERS);
However, the easiest way to get started is by using one of its linkify()
methods:
BetterLinkMovementMethod.linkify(Linkify.ALL, this)
.setOnLinkClickListener((textView, url) -> {
// Do something with the URL and return true to indicate that
// this URL was handled. Otherwise, return false to let Android
// handle it.
return true;
})
.setOnLinkLongClickListener((textView, url) -> {
// Handle long-clicks.
return true;
});
Download and source
compile 'me.saket:better-link-movement-method:2.2.0'
BetterLinkMovementMethod
is available on Github. Feel free to raise issues, send contributions or fork it for your own usage.