理论上这并不算是一个罕见的需求,毕竟在常见的场合下都需要部分文字可点击,还要分别跳转,对于Android开发来讲可谓是家常便饭。对于TextView的链接识别,另外还包括邮箱、电话号码等特殊内容都需要创建可点击事件,方法不算陌生,只需要在xml中的TextView标签里加上一句

android:autoLink="all"

这样TextView会主动将识别出来的链接、邮箱、电话号码等内容改变颜色和加上下划线来与其他内容进行区分。
但是这篇文章的目的在于点击拦截,因为如果是链接的话,系统默认点击后询问打开方式,也就是说会让你使用系统浏览器来进行打开链接,邮箱也是默认跳转系统邮件进行发送,电话号码就直接跳号码盘去拨打了。这些处理方式往往不能满足我们所需要的交互需求,所以我们需要对这些link的系统默认交互方式进行拦截和自定义。
另外,下划线实在是太烦人了,我们也需要去掉(确实麻烦,我就一起给处理了)。
首先,我们需要对内容进行转化成支付富文本的对象(Spannabel)

if (textView.getText() instanceof Spannable) {
    CharSequence text = textView.getText();
    int end = text.length();
    Spannable sp = (Spannable) text;
    URLSpan urls[] = sp.getSpans(0, end, URLSpan.class);
    //先识别出所有支持的链接(网址、电话、邮箱)
    SpannableStringBuilder style = new SpannableStringBuilder(text);
    for (final URLSpan urlSpan : urls) {
        //遍历所有链接,创建新的可被点击的Span对象
        ClickableSpan myURLSpan = new ClickableSpan() {
            @Override
            public void onClick(@NonNull View view) {
                //点击处理,这里可以通过urlSpan.getURL()来获取点击的是哪个链接
            }
        };
        //将新创见的Span对象替换原Span
        style.setSpan(myURLSpan, sp.getSpanStart(urlSpan), sp.getSpanEnd(urlSpan), Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
    }
    textView.setText(style);
}

接下来是如何去掉下划线
我们需要先创建一个没有下划线的Span样式

public class NoUnderlineSpan extends UnderlineSpan {
    @Override
    public void updateDrawState(TextPaint ds) {
        ds.setUnderlineText(false);
    }
}

非常讽刺的是,没有下划线的Span样式居然需要基于下划线Span样式创建,然后再画线的方法内将下划线设置为false……谷歌干嘛不直接开放这个方法呢???
然后我们再将这个Span样式应用到整个TextView上去

//这里的tv是我的TextView对象
CharSequence text = tv.getText();
if(text instanceof Spannable){
    Spannable spannable = (Spannable) tv.getText();
    NoUnderlineSpan noUnderlineSpan = new NoUnderlineSpan();
    spannable.setSpan(noUnderlineSpan,0,text.length(), Spanned.SPAN_MARK_MARK);
}

顺便提一句,我这里是全面不需要下划线,在setSpan的时候你可以根据你需要来定义去下划线的位置。
内容大概就是这样,回头我们聊聊Swift里面UILabel是怎么加链接和拦截点击的。