Android中TextView几乎是最常用的控件之一,但有时候常规的文本显示并不能满足我们对一些字符串的显示要求。例如我们想使得一个常规字符串中的网址、电话、邮箱变成可点击的链接,我们应该怎么处理呢?其实android为我们提供了类似的设置方法。
<TextView
android:id="@+id/tv_link"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoLink="all"
android:text="www.baidu.com abc de12345g ftp://ceshiwww ad 15232@qq.com"
android:textSize="20sp"
/>
其显示效果为:
其中的网址,数字,email都被识别为可点击的链接,点击相应的链接都会跳转到对应浏览器、电话拨号、邮箱进行相应的处理,如果你只希望处理里面某一个或某几个类型,你可已逐个添加android:autoLink=”web|email|phone|map”。当然,像一般的设置一样你也可以在java里面设置这一属性,Linkify.addLinks(tv_link, Linkify.ALL);两者得到的效果一样。其中有一点需要注意,如果使用setText设置text的值,Linkify.addLinks() 必须放在setText之后,否则看不到效果。
但有时我们可能想要实现一些自己定义的字符链接该怎么实现呢?比如我们想让上面显示的ftp://开始的字符串显示为超链接并且跳转到浏览器,该怎么实现呢。
Pattern p = Pattern.compile("ftp://\\S*",Pattern.CASE_INSENSITIVE);
Linkify.addLinks(tv_link, p, "http:");
其显示效果为:
好像很容易就实现了,那么现在我想让ftp://*的长度限制在10个字符以内该怎么办呢?其实改一下正则表达式就ok了。
Pattern p = Pattern.compile("ftp://\\S{0,4}\\s",Pattern.CASE_INSENSITIVE);
其实也是很简单,但如果再改一些难一点的正则表达式就有种要跪的感觉。那还有没有别的方式呢,当然了,既然这么问肯定是有的。看一下Linkify.addLinks的方法会发现addLinks(TextView text, Pattern p, String scheme,MatchFilter matchFilter, TransformFilter transformFilter) 这个方法我们可以利用其中的MatchFilter来实现想达到的目的。还是直接上代码:
private Linkify.MatchFilter mFtpMatchFilter = new MatchFilter() {
@Override
public boolean acceptMatch(CharSequence s, int start, int end) {
// TODO Auto-generated method stub
String text=s.subSequence(start, end).toString();
if(text.length()>10)
{
return false;
}
return true;
}
};
Linkify.addLinks(tv_link, p, "http:", mFtpMatchFilter, null);
相信MatchFilter的作用已经很明确了,他就是用来判断可疑的目标字符串是不是真的是我们的目标字符串。其中acceptMatch会传入整个字符串,以及目标字符串的开始位置和结束位置如果是返回true,否则返回false。看似已经ok了,但当你点击链接进行跳转的时候不知到你发现没地址栏显示的地址为http:ftp://ceshi这个咋回事呢,这是因为scheme填写http:后系统会自动选择为ftp://ceshi进行浏览器跳转,但在浏览器应用跳转的同时,其会自定把识别的字符串家在scheme的后面,所有就成了我们看到的效果,真的好烦人呀。
那我怎么才能只要ftp后面的地址而不要ftp呢,addlinks不是还有一个参数嘛,他就可以帮助我们完成这个事,还是来代码:
private final Linkify.TransformFilter mFtpTransformFilter = new TransformFilter() {
@Override
public String transformUrl(Matcher match, String url) {
String text = url.replaceAll("ftp:","");
return text;
}
};
Linkify.addLinks(tv_link, p, "http:", null, mFtpTransformFilter );
测试一下,显示果然ok了。TransformFilter的作用就是transformUrl用来处理返回给跳转应用的最终string。但现在问题是我明明想处理的是ftp,你让我跳转到浏览器干啥,好吧那我们就跳转到对应的ftp处理程序去。
首先,改一下跳转
Linkify.addLinks(tv_link, p, "ftp");
然后在AndroidManifest文件中注册一下ftp的处理activity:
<activity
android:name="com.example.linktest.ftpActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<!--隐式调用时,必须声明DEFAULT种类,否则Android不会启动该Activity-->
<category android:name="android.intent.category.DEFAULT" />
<!--声明能够处理模式ftp-->
<data android:scheme="ftp" />
</intent-filter>
</activity>
最后在ftpActivity中接收值。
Uri uri = getIntent().getData();
其实除了这个以外我们还可已使用Spannable来处理string,比如说我给整给字符串加了一个title,当点击title进行跳转
String title="title";
String sp=title+"\nwww.baidu.com abc de12345g ftp://ceshiwww ad 15232@qq.com";
SpannableString ss = SpannableString.valueOf(sp);
ss.setSpan(new URLSpan("tel:1111111"), 0,title.length(),Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
tv_link.setText(ss);
tv_link.setMovementMethod(LinkMovementMethod.getInstance());
运行界面如下
点击title时跳转到拨号界面,但有一个问题,Linkify.addLinks(tv_link, Linkify.ALL);好像失效了,网址和email的显示都没有变化。反正我没有找到原因,然后又加了Pattern用以识别,下面是识别email的Pattern,
Pattern p1 = Pattern.compile("[0-9a-zA-Z]+\\@[0-9a-zA-Z]+(\\.[0-9azA-Z]+)*",Pattern.CASE_INSENSITIVE);
Linkify.addLinks(tv_link, p1, "mailto:");
其实Spannable的操作也还是很丰富的,比如我们可以覆写点击方法,以上面操作为例,ss.setSpan中URLSpan改为
private class MyUrlSpan extends URLSpan {
public MyUrlSpan(String url) {
super(url);
}
@Override
public void onClick(View widget) {
Toast.makeText(MainActivity.this, "ceshi", Toast.LENGTH_SHORT).show();
super.onClick(widget);
}
}
点击后,跳转之前会弹出toast。SpannableString.setSpan()还有很多内容,可以设置非常多的显示内容,有兴趣的可以自己好好研究一下。