需求如下:用一个view展示"请点击https://mp.csdn.net进行查找"这句话,并点击链接地址时可以进行跳转。
最近接到类似的这种需求,网上查找资料学习到了TextView的autoLink属性,那autoLink是怎么使用的呢?为什么设置autoLink就可以实现TextView的超链接,底层是怎么实现的呢?TextView显示时自动排版不整齐,怎么解决呢?

TextView的autoLink属性的使用

TextView的autoLink的属性可以将符合指定格式的文本转换为可单击的超链接形式,有以下几种格式:
1、none:表示不进行任何匹配,默认;
2、web:表示匹配Web Url,如:内容中的http://www.baidu.com会成为可单击跳转的超链接;
3、email:表示匹配邮件地址:如:邮件地址为hello@com.cn会成为可单击的超链接;
4、phone:表示匹配电话号码:如:点击号码10086会跳到拨号界面;
5、map:表示匹配地图地址;
6、all:表示将会匹配web、email、phone、map;
以Web格式为例,在xml文件中使用:

<TextViewandroid:id="@+id/message"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="message"android:gravity="center_horizontal"android:autoLink="web"/>

运行效果:
展示时:

点击时:用浏览器打开链接地址

这时如果需求要求链接地址显示下划线,显示链接地址颜色为其他颜色,而且要求用app打开链接地址即用webview显示链接?那这时候怎么办呢?
显示效果如下:

需要拦截链接地址,给链接地址设置ClickableSpan样式,具体如下:
链接地址显示下划线:重写ClickableSpan方法,设置ds.setUnderlineText(true);
设置显示链接地址颜色:设置textview的setLinkTextColor属性;
设置链接地址点击事件:重写ClickableSpan的方法onClick自定义链接的点击事件;
例子:给链接地址设置显示下划线,设置显示颜色为绿色,设置点击链接地址时用webview显示
MainActivifty.java中:

private void showToolMethodDialog(){View view = LayoutInflater.from(this).inflate(R.layout.dialog_layout2, null);TextView textView = view.findViewById(R.id.message);TextView close = view.findViewById(R.id.close);textView.setText(text);textView.setLinkTextColor(getResources().getColor(R.color.color_03bf6d));//设置链接地址显示颜色AutoLinKTextViewUtil.getInstance().interceptHyperLink(textView);//拦截链接地址,设置显示样式和点击事件final AlertDialog dialog = new AlertDialog.Builder(this).create();dialog.setTitle("工具类方式test");dialog.setView(view);close.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {dialog.cancel();}});dialog.show();}

AutoLinkTextViewUtil.java:

public class AutoLinKTextViewUtil {private volatile static AutoLinKTextViewUtil autoLinKTextViewUtil;private AutoLinKTextViewUtil(){};public static AutoLinKTextViewUtil getInstance(){if(autoLinKTextViewUtil == null){synchronized (AutoLinKTextViewUtil.class){if(autoLinKTextViewUtil == null){autoLinKTextViewUtil = new AutoLinKTextViewUtil();}}}return autoLinKTextViewUtil;}public void interceptHyperLink(TextView textView) {textView.setMovementMethod(LinkMovementMethod.getInstance());CharSequence text = textView.getText();if (text instanceof Spannable) {int end = text.length();Spannable spannable = (Spannable) textView.getText();URLSpan[] urlSpans = spannable.getSpans(0, end, URLSpan.class);if (urlSpans.length == 0) {return;}SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(text);// 循环遍历并拦截 所有http://开头的链接for (URLSpan uri : urlSpans) {String url = uri.getURL();if (url.indexOf("http://") == 0) {CustomUrlSpan customUrlSpan = new CustomUrlSpan(textView.getContext(), url,new CustomUrlSpan.OnClickInterface() {@Overridepublic void onClick(View widget, String url, Context context) {//处理链接地址的点击事情if(!TextUtils.isEmpty(url)){Toast.makeText(widget.getContext(), url, Toast.LENGTH_SHORT).show();Intent intent = new Intent(widget.getContext(), WebViewActivity.class);intent.putExtra("url", url);widget.getContext().startActivity(intent);}}});spannableStringBuilder.setSpan(customUrlSpan, spannable.getSpanStart(uri),spannable.getSpanEnd(uri), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);}}textView.setText(spannableStringBuilder);}}
}

CustomUrlSpan.java:

public class CustomUrlSpan extends ClickableSpan {private Context context;private String url;private OnClickInterface onClickInterface;public CustomUrlSpan(Context context,String url, OnClickInterface onClickInterface){this.context = context;this.url = url;this.onClickInterface = onClickInterface;}@Overridepublic void updateDrawState(TextPaint ds) {ds.setUnderlineText(true);//设置显示下划线}@Overridepublic void onClick(View widget) {//链接地址点击事件监听if(onClickInterface != null){onClickInterface.onClick(widget, url, context);}}interface OnClickInterface{void onClick(View widget, String url, Context context);}

注意:Textview识别不出来字符串中的链接地址时,需要在链接地址前后加空格,例如:
private String text = “1.测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试 http://www.baidu.com 网址”;

从源码入手分析TextView的autoLink属性

为什么设置autoLink属性后就可以给TextView设置超链接呢,为什么重写ClickableSpan的方法就可以设置超链接显示样式和处理链接的点击事情呢?还是从源码入手进行分析:
TextView的源码:
在构造方法中搜索autoLink可以看到:

case com.android.internal.R.styleable.TextView_autoLink:mAutoLinkMask = a.getInt(attr, 0);break;

在构造方法中获取设置的属性autoLink并赋值给了变量mAutoLinkMask,然后我们搜索mAutoLinkMask变量,在setText方法中发现如下代码:

private void setText(CharSequence text, BufferType type,boolean notifyBefore, int oldlen) {//省略若干代码if (mAutoLinkMask != 0) {Spannable s2;if (type == BufferType.EDITABLE || text instanceof Spannable) {s2 = (Spannable) text;} else {s2 = mSpannableFactory.newSpannable(text);}if (Linkify.addLinks(s2, mAutoLinkMask)) {text = s2;type = (type == BufferType.EDITABLE) ? BufferType.EDITABLE : BufferType.SPANNABLE;/** We must go ahead and set the text before changing the* movement method, because setMovementMethod() may call* setText() again to try to upgrade the buffer type.*/setTextInternal(text);// Do not change the movement method for text that support text selection as it// would prevent an arbitrary cursor displacement.if (mLinksClickable && !textCanBeSelected()) {setMovementMethod(LinkMovementMethod.getInstance());}}}//省略若干代码}

上边代码中有一个Linkify.addLinks(s2, mAutoLinkMask)判断,那么这个方法作用是干什么的呢?
跟代码,查看addLinks方法,如下:

private static boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask,@Nullable Context context) {if (mask == 0) {return false;}URLSpan[] old = text.getSpans(0, text.length(), URLSpan.class);for (int i = old.length - 1; i >= 0; i--) {text.removeSpan(old[i]);}ArrayList<LinkSpec> links = new ArrayList<LinkSpec>();if ((mask & WEB_URLS) != 0) {//判断是否有web属性gatherLinks(links, text, Patterns.AUTOLINK_WEB_URL,new String[] { "http://", "https://", "rtsp://" },sUrlMatchFilter, null);//收集链接地址}if ((mask & EMAIL_ADDRESSES) != 0) {gatherLinks(links, text, Patterns.AUTOLINK_EMAIL_ADDRESS,new String[] { "mailto:" },null, null);}if ((mask & PHONE_NUMBERS) != 0) {gatherTelLinks(links, text, context);}if ((mask & MAP_ADDRESSES) != 0) {gatherMapLinks(links, text);}pruneOverlaps(links);if (links.size() == 0) {return false;}for (LinkSpec link: links) {//循环LinkSpec对象applyLink(link.url, link.start, link.end, text);//设置显示样式}return true;}

其中gatherLinks方法,根据正则表达式收集链接地址,组建LinkSpec对象集合,源码如下:

private static final void gatherLinks(ArrayList<LinkSpec> links,Spannable s, Pattern pattern, String[] schemes,MatchFilter matchFilter, TransformFilter transformFilter) {Matcher m = pattern.matcher(s);while (m.find()) {int start = m.start();int end = m.end();if (matchFilter == null || matchFilter.acceptMatch(s, start, end)) {LinkSpec spec = new LinkSpec();String url = makeUrl(m.group(0), schemes, m, transformFilter);spec.url = url;spec.start = start;spec.end = end;links.add(spec);}}}

其中applyLink方法,设置显示样式,源码如下:

private static final void applyLink(String url, int start, int end, Spannable text) {URLSpan span = new URLSpan(url);text.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);//设置显示样式}

这个时候觉得豁然开朗,可以明白autoLink的核心就是通过正则表达式收集链接地址,构造LinkSpec对象集合,通过调用text.setSpan方法设置样式显示。
那链接地址的点击事件默认用浏览器打开,是在哪里处理的呢?
在上面applyLink方法中有URLSpan,我们查看下URLSpan源码,如下:

public class URLSpan extends ClickableSpan implements ParcelableSpan {private final String mURL;/*** Constructs a {@link URLSpan} from a url string.** @param url the url string*/public URLSpan(String url) {mURL = url;}/*** Constructs a {@link URLSpan} from a parcel.*/public URLSpan(@NonNull Parcel src) {mURL = src.readString();}@Overridepublic int getSpanTypeId() {return getSpanTypeIdInternal();}/** @hide */@Overridepublic int getSpanTypeIdInternal() {return TextUtils.URL_SPAN;}@Overridepublic int describeContents() {return 0;}@Overridepublic void writeToParcel(@NonNull Parcel dest, int flags) {writeToParcelInternal(dest, flags);}/** @hide */@Overridepublic void writeToParcelInternal(@NonNull Parcel dest, int flags) {dest.writeString(mURL);}/*** Get the url string for this span.** @return the url string.*/public String getURL() {return mURL;}@Overridepublic 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);} catch (ActivityNotFoundException e) {Log.w("URLSpan", "Actvity was not found for intent, " + intent.toString());}}
}

可以看到URLSpan继承了ClickableSpan,在onClick方法中处理点击事情,所以如果需要修改链接的点击事件,我们需要继承ClickableSpan或是URLSpan,重写onClick事件。

说明

在上面方法中是直接调用一个方法interceptHyperLink来实现的拦截链接地址,修改链接地址的点击事件,有时候会觉得很麻烦,在想能不能通过自定义view继承TextView的方式实现这种效果呢,答案是肯定可以啊,原理都是一样的,具体可以查看这篇文章。
注明:关于TextView的autoLink属性设置超链接的完整demo请点击这里。

参考博客

https://blog.csdn.net/zhangjinhuang/article/details/52416608
https://blog.csdn.net/sahadev_/article/details/53639168
https://blog.csdn.net/ziyiwangchen/article/details/51861222

TextView的autoLink属性设置超链接问题相关推荐

  1. TextView的autoLink属性

    TextView的autoLink属性的作用是当TextView的内容包含一个URL或一个E-mail或者电话号码时,设置这个属性可以突出显示(加下划线以及更改字体颜色)这些内容,并且当用户单击这些突 ...

  2. TextView 的 AutoLink 属性和 onLongClick 冲突

    方法一 https://www.jianshu.com/p/0d15acb831be 在 TextView 需要设置 AutoLink 的属性来标记 'phone' . 'web' .'email' ...

  3. TextView使用textApperance属性设置字体颜色失效

    最近在开发中遇到一个问题,某个textview在textApperance中设置了字体颜色样式,但实际并不生效. 后来查了一下,原来是我的工程中的主题中定义了android:textColor,是这个 ...

  4. Android之TextView设置autoLink属性后自定义跳转到指定界面

    在TextView 中设置autoLink 属性可以自动识别Web URL.电话号码.电子邮件地址.添加下划线改变字体颜色并实现点击事件,支持自动识别的类型: android:autoLink=&qu ...

  5. android:autolink 颜色,Android设置完autoLink属性后自定义跳转到指定界面

    在TextView 中设置autoLink 属性可以自动识别Web URL,电话号码,电子邮件地址添加下划线改变字体颜色并实现点击事件,支持自动识别的类型: android:autoLink=&quo ...

  6. Android 设置完autoLink属性后自定义跳转到指定界面

    在TextView 中设置autoLink 属性可以自动识别Web URL,电话号码,电子邮件地址添加下划线改变字体颜色并实现点击事件,支持自动识别的类型: android:autoLink=&quo ...

  7. android开发 textview设置超链接颜色和背景色(被点击时的背景色、高亮色)

    安卓中textview设置超链接的代码网上也有一大堆,这里就不说了. 先说说设置超链接的文字的颜色,先定义MyURLSpan类并继承URLSpan类并重写updateDrawState()方法,jav ...

  8. Android TextView 属性设置

    2019独角兽企业重金招聘Python工程师标准>>> android textview xml 属性设置 android:ems 设置TextView的宽度为N个字符的宽度. an ...

  9. 如果希望单击超链接打开新的html,【单选题】如果希望单击超链接打开新的html页面,则需将target属性设置为 A. _blank B. _top C. _parent D. _self...

    [单选题]如果希望单击超链接打开新的html页面,则需将target属性设置为 A. _blank B. _top C. _parent D. _self 更多相关问题 一个球状蛋白质,含100个氨基 ...

最新文章

  1. 当程序员的一个人无聊时,甚至用Python开发出机器人看他们聊天
  2. 微软开发中心的rss历史记录(24)
  3. ecshop数据表结构说明
  4. Javascript综合应用小案例(续)
  5. 手机版python配置_appium+python 连接手机设备的yaml配置文件
  6. 目前市场上的电脑一体机从计算机种类,一体机电脑与普通电脑的区别
  7. svn mysql认证_SVN基于MySQL认证
  8. 点云着色系列之按坐标轴着色效果展示
  9. DWR中引用JS的路径问题
  10. RK3288 制作内核开机logo
  11. ‘sort’命令的14个有用的范例
  12. html转json有危险字符,javascript
  13. 解决vscode打开txt文件乱码
  14. Pearson 相关系数
  15. 四金及个人所得税的计算方法
  16. zabbix-邮件报警配置
  17. rust进水器怎么用_净水器的使用方法和注意事项
  18. 看到大神移植系统了,我来整理下市面上的系统...
  19. 人工智能学习联盟免费课程——案例三:BMR计算器
  20. 模拟jd快递单号查询

热门文章

  1. 浏览器F12定位悬浮下拉框元素
  2. 前端map循环遍历使用
  3. 爆料:苹果CMS原官方域名被盗,远程api被黑客利用!
  4. 什么是JSON schema,JSON schema也是JSON格式,刚开始学JSON,把JSON和JSON schema搞混了
  5. Mac 开发的神秘面纱:后娘养的嫡长子
  6. C语言中const、auto、register、volatile和typedef、枚举、联合体的使用和注意事项
  7. 百度mysql_如何连接百度Mysql_MySQL
  8. c语言 库 科学计算,C语言科学计算器.txt
  9. oracle中sql中文乱码,oracle中文字符乱码终极解决
  10. JAVA类与面向对象