自己代码中的示例:
SpannableString spanString = new SpannableString("网络超时,再试一次");
URLSpan span = new URLSpan("#") {
@Override
public void onClick(View widget) {
startRecognition();
}
};
spanString.setSpan(span, 5, 9, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mErrorRes = spanString;//mErrorRes 赋到 textview中
textview:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="14dp"
android:layout_marginRight="14dp"
android:background="@null"
android:gravity="center"
android:tag="error_tips"
android:textColor="#3c3c3c"
android:textSize="19sp" />
一、 Android的TextView中展示超链接有三种方式:
1. 使用Html.fromHtml(source)方法将html的如下文本转换为android支持的Spannable
Spanned spanned = Html.fromHtml("<ahref=\"http://www.google.com\">谷歌</a>");
textView.append(spanned);
2. 在java代码中使用UrlSpan
SpannableString ss = new SpannableString("谷歌");
ss.setSpan(newURLSpan("http://www.google.com\"), 0, 2,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(ss);
3. 直接将带有url的字符串设置到TextView中
textView.setText("http://www.google.com\");前提是已设置TextView的autoLink属性
或textView.append(Linkify.addLinks("http://www.google.com\",Linkify.WEB_URLS));无需设置autoLink属性
二、 设置TextView的autoLink属性的方法:
<TextView
android:id="@+id/testweb"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:autoLink="web" //是将文本的web网址解释成超链接,也可以使用TextView.setAutoLinkMask(mask)方法设置该属性
android:text="@string/link_text_auto"/>
三、 我在开发过程中遇到的问题
类似微博、贴吧的android客户端开发时,对于超链接富文本在android端我直接使用上面的方法2(UrlSpan)展示所有帖子的超链接。
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder();
spannableStringBuilder.append(urlSpan);
textView.setText(spannableStringBuilder);
未设置autoLink属性,超链接正常展示。但突然有一天发现TextView中有一个url纯文本未展示位超链接。
最近项目中遇到一个问题,卡了半天,和大家分享下。
我们的android应用需要在TextView中展示超链接(与3ms写博客中的超链接功能一样),这个超链接也是用户设置的包括超链接的显示名称和url。富文本格式为[url=www.baidu.com]百度[/url]
1 | SpannableStringBuilder sb = new SpannableStringBuilder(); |
2 | URLSpan span = new URLSpan(url); |
3 | SpannableString ss = new SpannableString(value); |
4 | ss.setSpan(span, 0 , value.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
上面的TextView是用来展示帖子的全部内容的,正常情况下超链接都显示OK。但若帖子内容中用户直接粘贴了链接的url而没有使用[url]富文本格式,此时该url就不能自动识别、和展示位超链接了。
于是在网上搜索了下,发现要实现自动识别url很简单,只要设置TextView的autoLink属性为"web"即可。于是乎我设置了android:autoLink="web",发现url可以自动识别为超链接,但突然使用UrlSpan设置的超链接没了,这么诡异。。。。
查看了源码才发现,先看看setText()方法实现
01 | private void setText(CharSequence text, BufferType type, boolean notifyBefore, int oldlen) { |
03 | if (mAutoLinkMask != 0 ) { |
06 | if (type == BufferType.EDITABLE || text instanceof Spannable) { |
07 | s2 = (Spannable) text; |
09 | s2 = mSpannableFactory.newSpannable(text); |
12 | if (Linkify.addLinks(s2, mAutoLinkMask)) { |
14 | type = (type == BufferType.EDITABLE) ? BufferType.EDITABLE :BufferType.SPANNABLE; |
25 | if (mLinksClickable && !textCanBeSelected()) { |
26 | setMovementMethod(LinkMovementMethod.getInstance()); |
上面可以看到mAutoLinkMask属性(可以再xml中设置android:autoLink="web"或使用TextView.setAutoLink()方法设置)不为空时,使用Linkify.addLinks(s2, mAutoLinkMask)将TextView文本中的url自动识别并转换为UrlSpan.
02 | * Scans the text of the provided Spannable and turns all occurrences |
03 | * of the link types indicated in the mask into clickable links. |
04 | * If the mask is nonzero, it also removes any existing URLSpans |
05 | * attached to the Spannable, to avoid problems if you call it |
06 | * repeatedly on the same text. |
08 | public static final boolean addLinks(Spannable text, int mask) { |
13 | URLSpan[] old = text.getSpans( 0 , text.length(), URLSpan. class ); |
15 | for ( int i = old.length - 1 ; i >= 0 ; i--) { |
16 | text.removeSpan(old[i]); |
19 | ArrayList<LinkSpec> links = new ArrayList<LinkSpec>(); |
21 | if ((mask & WEB_URLS) != 0 ) { |
22 | gatherLinks(links, text, Patterns.WEB_URL, |
24 | sUrlMatchFilter, null ); |
27 | if ((mask & EMAIL_ADDRESSES) != 0 ) { |
28 | gatherLinks(links, text, Patterns.EMAIL_ADDRESS, |
29 | new String[] { "mailto:" }, |
33 | if ((mask & PHONE_NUMBERS) != 0 ) { |
34 | gatherLinks(links, text, Patterns.PHONE, |
35 | new String[] { "tel:" }, |
36 | sPhoneNumberMatchFilter, sPhoneNumberTransformFilter); |
39 | if ((mask & MAP_ADDRESSES) != 0 ) { |
40 | gatherMapLinks(links, text); |
45 | if (links.size() == 0 ) { |
49 | for (LinkSpec link: links) { |
50 | applyLink(link.url, link.start, link.end, text); |
上面是Linkify.addLinks()方法源码,其中做了三件事情:
1.删除text文本中原有的Spanable
2.使用gatherLinks()是使用正则表达式匹配各种类型的url
3.最终使用applyLink()将2中匹配到的url生成UrlSpan。
下面是Linkify.applyLink()源码
1 | private static final void applyLink(String url, int start, int end, Spannable text) { |
2 | URLSpan span = new URLSpan(url); |
4 | text.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); |
可以看出
Linkify.applyLink()方法其实就是将前面使用正则表达式匹配出来的各种类型(
WEB_URLS、
EMAIL_ADDRESSES、
MAP_ADDRESSES等
)超链接字符串来构造UrlSpan。
这也就解释了为何我设置了autoLink属性,但我自行构造的UrlSpan都不展示为超链接的原因了,被删除了。
再看看TextView::append()方法
02 | * Convenience method: Append the specified text slice to the TextView's |
03 | * display buffer, upgrading it to BufferType.EDITABLE if it was |
04 | * not already editable. |
06 | public void append(CharSequence text, int start, int end) { |
07 | if (!(mText instanceof Editable)) { |
08 | setText(mText, BufferType.EDITABLE); |
11 | ((Editable) mText).append(text, start, end); |
可以看出,首次调用append()方法首先会把TextView的BufferType设置为BufferType.EDITABLE。然后追加文本,并且追加文本时不会进行Linkify.addLinks()操作,也即append()追加的文本不会自动识别url。
我最终实现超链接方法如下:
04 | if (!TextUtils.isEmpty(url)) { |
05 | URLSpan span = new URLSpan(url); |
06 | SpannableString ss = new SpannableString(value); |
07 | ss.setSpan(span, 0 , value.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
11 | URLSpan span = new URLSpan(attachmentUrl); |
12 | SpannableString ss = new SpannableString(attachmentName); |
13 | ss.setSpan(span, 0 , attachmentName.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
17 | SpannableString spannableString = new SpannableString(textElement.getValue()); |
18 | Linkify.addLinks(spannableString, Linkify.WEB_URLS | Linkify.EMAIL_ADDRESSES); |
19 | textView.append(spannableString); |
即自定调用Linkify.addLink()方法转化url字符串为UrlSpan。
问题2:自定义超链接字体颜色
方法1:继承URLSpan类并复写updateDrawState()方法
@Override
public void updateDrawState(TextPaint ds) {
ds.setColor(Color.rgb(40, 192, 198));//#28C0C6
ds.setUnderlineText(true);
}
当然也可以复写URLSpan::onClick()自定义超链接的点击事件处理。
方法2:设置TextView的android:textAppearance属性
android:textAppearance="@style/CustomTextAppearance"
或textView.setTextAppearance(mContext, R.style.CustomTextAppearance);
<style name="CustomTextAppearance" parent="@android:style/TextAppearance.Holo">
<item name="android:textColor">@color/text_color_dark_black</item>
<item name="android:textColorLink">@color/text_color_blue</item>
</style>