Android千变万化TextView(SpannableString全解析)

上一篇博客讲了怎么用SpannableString实现表情、文字混合显示的EditText和TextView。
其实显示表情只是SpannableString的一个小功能,它的强大之处今天将完全展示出来。利用SpannableString,我们可以对一个TextView中的每一个字符做各种各样的变化。

老规矩,在本期节目开始之前,先来一个搞笑段子:

前阵子电脑中病毒了,是一个流氓软件,每天定时在后台安装程序。我也不懂电脑就没管它。
直到有一天它自己安装了个360,然后360扫描出了这个流氓软件,把它卸载了。。。

首先来看一下效果图
这里写图片描述

这里总共20行,每一行都是TextView(除了最后一个),只是SpannableString设置了不同span。

下面来一个一个解释每一种样式的实现方式:

1)默认样式
不解释。。。就是啥都不设置。

2)自定义字体

SpannableString ss1 = new SpannableString(txCustomTypeface.getText());
ss1.setSpan(new TypefaceSpan("serif"), 0, txCustomTypeface.getText().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
txCustomTypeface.setText(ss1);

自定义样式对应的Span为TypefaceSpan,它的构造函数传的是字体样式,总共有5种:
default,default-bold,monospace,serif,sans-serif

3)字体绝对大小

SpannableString ss2 = new SpannableString(txAbsoluteSize.getText());
ss2.setSpan(new AbsoluteSizeSpan(16, true), 0, txAbsoluteSize.getText().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
txAbsoluteSize.setText(ss2);

它对应的Span是AbsoluteSizeSpan,构造函数第一个参数是字体大小,第二个参数表示是否转换为dp。
比如第一个参数是20,第二个参数如果为true,则表示20dp, 否则表示20px。

4)字体相对大小

SpannableString ss3 = new SpannableString(txRelativeSize.getText());
ss3.setSpan(new RelativeSizeSpan(1.5f), 0, txRelativeSize.getText().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
txRelativeSize.setText(ss3);

这个和上面类似,表示字体大小是默认大小的几倍。

5)字体前景色

SpannableString ss4 = new SpannableString(txForegroundColor.getText());
ss4.setSpan(new ForegroundColorSpan(Color.BLUE), 0, txForegroundColor.getText().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
txForegroundColor.setText(ss4);

它对应的Span是ForegroundColorSpan,参数传颜色值,一目了然。

6)字体背景色

SpannableString ss5 = new SpannableString(txBackgroundColor.getText());
ss5.setSpan(new BackgroundColorSpan(Color.LTGRAY), 0, txBackgroundColor.getText().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
txBackgroundColor.setText(ss5);

前景色明白了,背景色就不用解释了吧。

7)粗斜体

SpannableString ss6 = new SpannableString(txBordAndItalic.getText());
ss6.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), 0, txBordAndItalic.getText().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
txBordAndItalic.setText(ss6);

它对应的是StyleSpan,参数是样式,总共有4种:
Typeface.NORMAL:普通
Typeface.BORD:粗体
Typeface.ITALIC:斜体
Typeface.BORD_ITALIC:粗斜体

8)下划线

SpannableString ss7 = new SpannableString(txUnderLine.getText());
ss7.setSpan(new UnderlineSpan(), 0, txUnderLine.getText().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
txUnderLine.setText(ss7);

看了这么多,是不是发现用法都一样。。。只是Span对象不一样而已。

9)删除线

SpannableString ss8 = new SpannableString(txDeleteLine.getText());
ss8.setSpan(new StrikethroughSpan(), 0, txDeleteLine.getText().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
txDeleteLine.setText(ss8);

不解释了。

10)上标、下标

SpannableString ss9 = new SpannableString(txSubSuperScript.getText());
ss9.setSpan(new SuperscriptSpan(), 2, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
ss9.setSpan(new SubscriptSpan(), 5, 6, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
txSubSuperScript.setText(ss9);

这里需要注意,这里两个span对应的是上标和下标的字符。

11)超链接形式

这里需要重点解释下,超链接对应的Span叫做URLSpan,它有6中表现形式:
电话、邮件、网址、短信、彩信、地图
它们都是URLSpan,只是参数的格式不一样。
电话:”tel:02512345678”
邮件:”mailto:[email protected]
网址:”http://www.baidu.com
短信:”sms:02512345678”
彩信:”mms:02512345678”
地图:”geo:30.123456,-50.024456”

不同的格式有什么用呢?答案是这样的。
指定URLSpan的TextView是有点击效果的,比如:
指定电话URLSpan,点击会自动跳转到电话App,并且携带的就是URL中的号码。
其它的类似,都会跳转对应的App。

这个明白了,下面6段代码就很简单啦!

SpannableString ss10 = new SpannableString(txTelUrl.getText());
ss10.setSpan(new URLSpan("tel:02512345678"), 0, txTelUrl.getText().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
txTelUrl.setText(ss10);
txTelUrl.setMovementMethod(LinkMovementMethod.getInstance());

SpannableString ss11 = new SpannableString(txMailUrl.getText());
ss11.setSpan(new URLSpan("mailto:[email protected]"), 0, txMailUrl.getText().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
txMailUrl.setText(ss11);
txMailUrl.setMovementMethod(LinkMovementMethod.getInstance());

SpannableString ss12 = new SpannableString(txWebUrl.getText());
ss12.setSpan(new URLSpan("http://www.baidu.com"), 0, txWebUrl.getText().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
txWebUrl.setText(ss12);
txWebUrl.setMovementMethod(LinkMovementMethod.getInstance());

SpannableString ss13 = new SpannableString(txSmsUrl.getText());
ss13.setSpan(new URLSpan("sms:02512345678"), 0, txSmsUrl.getText().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
txSmsUrl.setText(ss13);
txSmsUrl.setMovementMethod(LinkMovementMethod.getInstance());

SpannableString ss14 = new SpannableString(txMmsUrl.getText());
ss14.setSpan(new URLSpan("mms:02512345678"), 0, txMmsUrl.getText().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
txMmsUrl.setText(ss14);
txMmsUrl.setMovementMethod(LinkMovementMethod.getInstance());

SpannableString ss15 = new SpannableString(txGeoUrl.getText());
ss15.setSpan(new URLSpan("geo:30.123456,-50.024456"), 0, txGeoUrl.getText().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
txGeoUrl.setText(ss15);
txGeoUrl.setMovementMethod(LinkMovementMethod.getInstance());

但是别急,你有没有发现这6段代码跟之前的有一点不一样?

是的,这6段代码每一段后面都加了一句:textView.setMovementMethod(LinkMovementMethod.getInstance());
这是必须要加的。不加是不能激活链接的。

12)项目符号

SpannableString ss16 = new SpannableString(txBullte.getText());
ss16.setSpan(new BulletSpan(0, Color.RED), 0, txBullte.getText().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
txBullte.setText(ss16);

它对应的是BulletSpan,第一个参数表示的是项目符号的宽度,第二个参数是项目符号的颜色。

13)横向拉伸

SpannableString ss17 = new SpannableString(txScaleX.getText());
ss17.setSpan(new ScaleXSpan(2), 0, txScaleX.getText().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
txScaleX.setText(ss17);

它对应的是ScaleXSpan,参数就是缩放的倍数。

你或许会想有ScaleX,肯定有ScaleY吧?
很遗憾,我没找到。。。

14)综合运用

ColorStateList csl1 = null;
try{
    XmlResourceParser xrp = getResources().getXml(R.drawable.text_csl);
    csl1 = ColorStateList.createFromXml(getResources(), xrp);
} catch (XmlPullParserException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

//依次包括字体名称,字体大小,字体样式,字体颜色,链接颜色
SpannableString ss18 = new SpannableString(txUseAll.getText());
ss18.setSpan(new TextAppearanceSpan("monospace", Typeface.BOLD, 50, csl1, csl1), 0, txUseAll.getText().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
txUseAll.setText(ss18);

这个Span和前面的不一样,它比较强大,可以一次性设置多项属性,在代码中也注释了,分别是:
字体名称,字体大小,字体样式,字体颜色,链接颜色。

这里我偷懒,字体颜色和链接颜色就用了同一个。

15)ColorStateList

最后来讲一下ColorStateList吧。它是干什么用的呢?先不急。

大家有没有遇到过这种需求:
写一个Button,正常状态背景颜色是白色,字体颜色是黑色;
手指按下时反过来,背景颜色是黑色,字体颜色是白色。

背景颜色很简单,用一个selector,然后button.setBackground(R.drawable.selector);
那字体颜色怎么办呢?没办法用selector,这时候就用到了ColorStateList。

看名字就知道,它是表示颜色和状态的一个集合。
再看它的创建方法:

XmlResourceParser xrp = getResources().getXml(R.drawable.text_csl);
csl1 = ColorStateList.createFromXml(getResources(), xrp);

哈哈,原来,它就是selector,只是穿了个马甲而已。。。

代码:

ColorStateList csl2 = null;
try {
    XmlResourceParser xrp = getResources().getXml(R.drawable.button_text);
    csl2 = ColorStateList.createFromXml(getResources(), xrp);
} catch (XmlPullParserException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}
button.setTextColor(csl2);

好了,全部讲完了,是不是觉得并不复杂,用法几乎是一毛一样的,只是Span对象不同而已。
这就是Android为我们封装的TextView,没想到这么强大吧!我们平时用的只是它最最简答的功能。

现在坚持有空就写写博客,敲一遍记录一遍,自己的记忆就更深刻,掌握的就更牢固。同样也能帮助到一些其它正在学习的人。我每天也在从别人那里学习到新的技术,互帮互助。

本期节目就到这里,感谢大家的收看,我们下期再见~

发布了40 篇原创文章 · 获赞 88 · 访问量 9万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章