TextView是用于文字的控件,一般可以在布局文件中设置text属性或者在代码中使用setText()方法。但如果想做到格式化文字,比如像网页中将其中的URL、手机号码等等显示不同颜色,并且设置点击事件,可以直接跳转到浏览器或者电话,该如何实现呢?本篇博客重点介绍这些应该如何实现。
使用autoLink属性
TextView的autoLink属性用于控制文本中的URL、Email地址等是否自动被发现并转化为可点击的链接。默认是”none”。可以设置none、web。email、phone、map以及all值,如果想设置多个值则使用”|”。
想了解更多的可以参考官方文档
使用autoLink显示URL和手机号码
<TextView
android:id="@+id/url_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoLink="all"
android:text="@string/url_text" />
<TextView
android:layout_below="@id/url_tv"
android:autoLink="all"
android:text="@string/tel_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
以下为字符串:
<string name="url_text"><a href="http://www.baidu.com">百度</a> http://www.baidu.com</string>
<string name="tel_text">tel:13007147721</string>
下图为运行效果,并且红色链接都是可以点击的,点击URL可以跳转到浏览器,点击手机号跳转到拨号器
使用textColorLink改变链接颜色
textColorLink可以以”@[+][package:]type:name”或”?[package:][type:]name”引用别的资源或主题属性,也可以直接设置颜色值。下面将链接改为蓝色,比较下效果:
<TextView
android:textColorLink="#0000FF"
android:id="@+id/url_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoLink="all"
android:text="@string/url_text" />
<TextView
android:layout_below="@id/url_tv"
android:autoLink="all"
android:text="@string/tel_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
效果图:
可以看到上面的链接颜色变为绿色,而下面还是默认的粉红色。
局限
只能显示url、email、phone等链接,像上述文字中将”百度”二字设为超链接,并不能点击。
使用Html文字
Html类用于将HTML字符串处理成可显示的格式化文字,但是并不是所有的标签都支持。那么我们只需要编辑Html文本,再设置给TextView显示就行了。
private String html = "<a href=\"http://www.baidu.com\">百度</a>"
+" <a href=\"tel:13007147721\">13007147721</a>"
+" <h1>H 1标题<h1>"
+" <h2>H 1标题<h2>"
+" <h3>H 1标题<h3>"
+" <h4>H 1标题<h4>"
+"<img src=\"smail.png\"><img>";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
urlTextView = (TextView) findViewById(R.id.url_tv);
urlTextView.setText(Html.fromHtml(html));
urlTextView.setMovementMethod(LinkMovementMethod.getInstance());
}
上述代码将html字符串转成Html文本后再设置给TextView,如果需要链接可以被点击,需要调用setMovementMethod(LinkMovementMethod.getInstance())。效果如下:
“百度”和“13007147721”称为超链接,并且可以点击,h1-h6标题是可以用的,其他标签没有做验证。img标签可以使用,但是上面可以看到我们想使用本地的图片但是没有显式出来。那么需要怎么做呢?
如何使用Html显式图片
Html类的文档可以参考
Html类有两个接口,一个ImageGetter用于为标签获取图片,一个TagHandler用于处理那些Html类不能处理的标签。
ImageGetter只有一个接口:
其中参数source就是img标签中的src属性的值。下面的代码用于显示上面的html文字中的图片
//显示图片
Spanned html = Html.fromHtml(text, new Html.ImageGetter() {
@Override
public Drawable getDrawable(String s) {
Drawable drawable = getResources().getDrawable(R.drawable.smail);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
return drawable;
}
}, new Html.TagHandler() {
@Override
public void handleTag(boolean b, String s, Editable editable, XMLReader xmlReader) {
}
});
urlTextView.setText(html);
有一点需要记住的是获取到Drawable,一定要记得调用setBounds方法设置图片的边界,否则不能显示图片。
效果如下:
如果有很多需要显示的项目中的文件,那么可以每个src中的属性设为R.drawable.xxx,那么ImageGetter的getDrawable就可以根据资源Id去获取图片了。
Html显示图片的实质
查看API,可以看到Html的转换方法的返回都是Spanned类型,那么Spanned类型是什么呢?下一部分将详细介绍Spanned格式化文字。
局限
可以格式化很多文本样式,但是需要将文字添加html标签,并且有些标签不支持。
使用Spanned格式化文字
类结构
CharSequence是一组可读的Char序列,提供了操作Char序列的接口。
Spanned可以在文本范围内添加标记。不是所有文本类都有可变的标记和文字。Spannable接口有添加或移除标记,而Editable有可变的文字。
Spannable用于添加标记和移除标记。并不是所有的Spannable都有可变的文字,Editable拥有可变的文字。
SpannableString文本不可变,但是可以添加标记和移除标记。
Editable是文本可以改变接口。
SpannableStringBuilder是Editable的实现类。
SpannableString用法
使用方法为新建一个SpannableString对象,并将文本传入,再调用setSpan()方法设置各个部分的样式,setSpan的声明如下:
void setSpan (Object what, int start, int end, int flags)
其中Object代表标记类型,可以使用的对象为CharacterStyle、ParagraphStyle的实现类,具体可以参考官方文档;start表示文本该标记作用的起始位置,end表示该标记作用的结束位置,flags参数可以参考Spanned类。我使用的都是SPAN_INCLUSIVE_EXCLUSIVE,该参数包含起始位置,不包含结束位置。下面为SpannableString的用法,几乎使用了所有的CharacterStyle。下面的代码分别设置了链接、前景、背景、删除线、下划线绝对尺寸、相对尺寸、图片、字体。
private String text = "百度"
+ "13007147721"
+ "前景"
+ "背景"
+ "删除线"
+ "下划线"
+ "图片"
+ "H1H2H3H4H5H6"
+ "H1H2H3H4H5H6"
+ "X3X2X1"
+ "字体"
+"正常字体";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
urlTextView = (TextView) findViewById(R.id.url_tv);
SpannableString spannableString = new SpannableString(text);
//设置URL链接
spannableString.setSpan(new URLSpan("http://www.baidu.com"), 0, 2, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//设置电话号码链接
spannableString.setSpan(new URLSpan("tel:13007147721"), 2, 13, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//设置前景色
spannableString.setSpan(new ForegroundColorSpan(Color.parseColor("#0000FF")), 13, 15, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//设置背景色
spannableString.setSpan(new BackgroundColorSpan(Color.RED), 15, 17, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//设置删除线
spannableString.setSpan(new StrikethroughSpan(), 17, 20, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//下划线
spannableString.setSpan(new UnderlineSpan(), 20, 23, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//设置图片
spannableString.setSpan(new ImageSpan(this, BitmapFactory.decodeResource(getResources(), R.drawable.smail)), 23, 25, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//设置文本大小
//绝对尺寸
spannableString.setSpan(new AbsoluteSizeSpan(100), 25, 27, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new AbsoluteSizeSpan(90), 27, 29, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new AbsoluteSizeSpan(80), 29, 31, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new AbsoluteSizeSpan(70), 31, 33, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new AbsoluteSizeSpan(60), 33, 35, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new AbsoluteSizeSpan(50), 35, 37, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//相对尺寸
spannableString.setSpan(new RelativeSizeSpan(6), 37, 39, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new RelativeSizeSpan(5), 39, 41, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new RelativeSizeSpan(4), 41, 43, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new RelativeSizeSpan(3), 43, 45, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new RelativeSizeSpan(2), 45, 47, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new RelativeSizeSpan(1), 47, 49, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//设置X方向的改变
spannableString.setSpan(new ScaleXSpan(3), 49, 51, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new ScaleXSpan(2), 51, 53, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new ScaleXSpan(1), 53, 55, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//设置字体
spannableString.setSpan(new TypefaceSpan("monospace"), 55, 57, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
urlTextView.setText(spannableString);
urlTextView.setMovementMethod(LinkMovementMethod.getInstance());
}
效果图:
SpannableString添加多个格式化对象效果会如何?
对于同一部分文字,只添加同一类型的对象一个,效果会将每个格式化对象的效果累加。下面是测试代码,对于“百度”两词,设置URLSpan、ForegroundColorSpan、BackgroundColorSpan。
//SpannableString设置多个Span
SpannableString spannableString = new SpannableString(text);
//设置URLSpan
URLSpan baiduSpan = new URLSpan("http://www.baidu.com");
spannableString.setSpan(baiduSpan, 0, 2, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//设置前景
spannableString.setSpan(new ForegroundColorSpan(Color.BLUE), 0, 2, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//设置背景
spannableString.setSpan(new BackgroundColorSpan(Color.RED), 0, 2, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
urlTextView.setText(spannableString);
urlTextView.setMovementMethod(LinkMovementMethod.getInstance());
下图是效果:
可以看到“百度”前景蓝色,背景红色,并且可以点击出百度首页。
对于同一部分文字,如果每个类型的对象都添加两个,那么效果会是怎样呢?先看代码和效果,其中代码中,分别设置了两个URLSpan、ForegroundColorSpan、BackgroundColorSpan。
//SpannableString设置多个Span
SpannableString spannableString = new SpannableString(text);
//设置URLSpan
URLSpan baiduSpan = new URLSpan("http://www.baidu.com");
URLSpan taobaoSpan = new URLSpan("http://www.baidu.com");
spannableString.setSpan(baiduSpan, 0, 2, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(taobaoSpan, 0, 2, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//设置前景
spannableString.setSpan(new ForegroundColorSpan(Color.BLUE), 0, 2, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new ForegroundColorSpan(Color.WHITE), 0, 2, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//设置背景
spannableString.setSpan(new BackgroundColorSpan(Color.RED), 0, 2, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new BackgroundColorSpan(Color.BLUE), 0, 2, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
urlTextView.setText(spannableString);
urlTextView.setMovementMethod(LinkMovementMethod.getInstance());
效果如下图:
可以看到文字颜色为白色,背景为黑色,可以看出是后设置的起了效果;但是点击后还是显示百度首页,而没有显示淘宝首页,说明URLSpan是前面那个起了效果。
SpannableString删除标记
SpannableString可以调用removeSpan来删除标记。下面的示例用于删除上述代码中的百度URLSpan,看下效果。
//SpannableString设置多个Span
SpannableString spannableString = new SpannableString(text);
//设置URLSpan
URLSpan baiduSpan = new URLSpan("http://www.baidu.com");
URLSpan taobaoSpan = new URLSpan("http://www.baidu.com");
spannableString.setSpan(baiduSpan, 0, 2, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(taobaoSpan, 0, 2, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//设置前景
spannableString.setSpan(new ForegroundColorSpan(Color.BLUE), 0, 2, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new ForegroundColorSpan(Color.WHITE), 0, 2, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//设置背景
spannableString.setSpan(new BackgroundColorSpan(Color.RED), 0, 2, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new BackgroundColorSpan(Color.BLUE), 0, 2, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//删除百度URLSpan
spannableString.removeSpan(baiduSpan);
urlTextView.setText(spannableString);
urlTextView.setMovementMethod(LinkMovementMethod.getInstance());
效果如上图,但是点击事件将出现淘宝首页。
SpannableString用法总结
SpannableString可以设置多种Span用来格式化文字,但是文字内容一旦设置后就不可以更改。如果对同一文字设置多个效果是可以的,但是有些效果是会覆盖之前的效果,比如ForgroundColorSpan等,而有的效果会使用第一个,比如URLSpan。
SpannableStringBuilder用法
SpannableStringBuilder用法和SpannableString用法类型。下面是将SpannableString替换为SpannableStringBuilder的代码和效果图:
//SpannableStringBuilder基本用法
SpannableStringBuilder spannableString = new SpannableStringBuilder(text);
URLSpan baiduSpan = new URLSpan("http://www.baidu.com");
//设置URL链接
spannableString.setSpan(baiduSpan, 0, 2, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//设置电话号码链接
spannableString.setSpan(new URLSpan("tel:13007147721"), 2, 13, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//设置前景色
spannableString.setSpan(new ForegroundColorSpan(Color.parseColor("#0000FF")), 13, 15, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//设置背景色
spannableString.setSpan(new BackgroundColorSpan(Color.RED), 15, 17, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//设置删除线
spannableString.setSpan(new StrikethroughSpan(), 17, 20, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//下划线
spannableString.setSpan(new UnderlineSpan(), 20, 23, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//设置图片
spannableString.setSpan(new ImageSpan(this, BitmapFactory.decodeResource(getResources(), R.drawable.smail)), 23, 25, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//设置文本大小
//绝对尺寸
spannableString.setSpan(new AbsoluteSizeSpan(100), 25, 27, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new AbsoluteSizeSpan(90), 27, 29, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new AbsoluteSizeSpan(80), 29, 31, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new AbsoluteSizeSpan(70), 31, 33, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new AbsoluteSizeSpan(60), 33, 35, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new AbsoluteSizeSpan(50), 35, 37, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//相对尺寸
spannableString.setSpan(new RelativeSizeSpan(6), 37, 39, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new RelativeSizeSpan(5), 39, 41, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new RelativeSizeSpan(4), 41, 43, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new RelativeSizeSpan(3), 43, 45, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new RelativeSizeSpan(2), 45, 47, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new RelativeSizeSpan(1), 47, 49, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//设置X方向的改变
spannableString.setSpan(new ScaleXSpan(3), 49, 51, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new ScaleXSpan(2), 51, 53, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(new ScaleXSpan(1), 53, 55, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//设置字体
spannableString.setSpan(new TypefaceSpan("monospace"), 55, 57, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
urlTextView.setText(spannableString);
urlTextView.setMovementMethod(LinkMovementMethod.getInstance());
SpannableStringBuilder的特别之处
SpannableStringBuilder设置完文本内容后,可以改变文本内容。下面的代码在上述代码之后再加入更改文字、插入文字的操作,并从新设置TextView的文字。
//改变文字
spannableString.replace(0, 2, "淘宝");
spannableString.insert(2, "电话号码:", 0, 5);
urlTextView.setText(spannableString);
效果图如下:
可以看到显示内容已经变为了“淘宝电话号码:13007147721”,但是对于电话号码而言,链接文字多了“电话号码:”部分,而其余部分效果并没有收到影响。下面试着将这些改变。首先将已有的链接对象移除后,再添加新的Span对象。将淘宝的链接改为”http://www.taobao.com“;将电话号码多余的效果去除
//更改效果
spannableString.removeSpan(baiduSpan);
spannableString.removeSpan(telSpan);
spannableString.setSpan(new URLSpan("http://www.taobao.com"), 0, 2, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(telSpan, 7, 18, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
urlTextView.setText(spannableString);
效果如下:
点击淘宝后会出现淘宝首页。
SpannableStringBuilder总结
用法和SpannableString用法相同,只不过增加了可以更改文本内容的接口。
总结
TextView显示丰富多彩的文字有很多中方式,如果只是一些简单的url、手机号,那么可以使用autoLink属性;如果文本本身就是html文档,那么可以使用Html类将文本转成Spanned后显示;如果想实现普通文本的格式化,那么应该使用SpannableString或SpannableStringBuilder。