不知道大家在工作中是否遇到过这种情况,明明就是一个字符串,但是设计师偏让我们显示成不同的样式,具体效果和下图类似
如果你用4个TextView去做的话,只能说可以做,但是代码被别人看到肯定要喷成翔。有人说用Html.fromHtml()方法来将文本刷成html格式,然后通过设置html样式的方式来做。这样不是不可以,首先我们不是很擅长html,其次这种方式提供的效果有限,很容易就卡壳了。这里我介绍一下用原生的动态样式字符串类SpannableStringBuilder类来实现各种各样不同的文字效果,感兴趣的同学赶快上车,一起来学习一下该如何优雅地实现这种效果吧!
SpannableStringBuilder介绍
SpannableStringBuilder是什么?
查看一下SpannableStringBuilder代码,它实现了CharSequence接口,这个接口就是字符串的父接口,说明它和字符串是功能类似的。可以这么认为,SpannableStringBuilder是字符串的升级版本,支持动态地设置字符串的各种各样的属性,具体可以设置哪些属性呢,下面一一道来。
这是SpannableStringBuilder的类继承:
下面是String的类继承:
SpannableStringBuilder是怎么使用的?
首先初始化一个SpannableStringBuilder,然后初始化一个Span,这个Span具体是哪个对象,也就是what对象依具体情况而定。比如想设置文字颜色是传入ForegroundColorSpan,如果想插入图片就传入ImageSpan即可。初始化的span通过SpannableStringBuilder对象的setSpan方法传入,最后直接通过TextView的setText()将SpannableStringBuilder对象传入即可。注意,setSpan的第二个参数和第三个参数分别代表起始文字,以及影响到的结束文字,包括前面不包括后面文字。
void setSpan (Object what, int start, int end, int flags)
SpannableStringBuilder ssb = new SpannableStringBuilder("天王盖地虎!");
XXSpan Span = new XXSpan();
ssb.setSpan(span,0,1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mTv.setText(ssb);
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE是什么?
这是flgs中的一种,flags总共包括以下四种。可能大家光看文字肯定看晕了,我这里就举个例子吧,分别让大家看一下这四种flag设置以后的效果。
假设最初的文本样式时这样的:
接下来我在追风的前后分别插入一个字,效果就很明显了,插入代码如下:
mSSB.insert(0,"哼");
mSSB.insert(3,"哈");
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE:前后都不包括(在标志位【start,end)前后添加文字,新添加的文字不会有任何设置的属性)
Spannable.SPAN_EXCLUSIVE_INCLUSIVE :前面不包括,后面包括。(在标志位【start,end)前添加文字,新添加的文字不会有任何设置的属性,后边的添加的文字会带有设置的what属性)
Spannable.SPAN_INCLUSIVE_EXCLUSIVE :前面包括,后面不包括。(在标志位【start,end)后添加文字,新添加的文字不会有任何设置的属性,前边边的添加的文字会带有设置的what属性)
Spannable.SPAN_INCLUSIVE_INCLUSIVE :前后都包括。前后都不包括(在标志位【start,end)前后添加文字,新添加的文字会有设置的属性)
Span是什么?
Span是谷歌提供的专门用来代表字符串样式的一个抽象,可以非常完美地配合SpannableStringBuilder进行使用,就相当于是一个本身可以设置参数的高级参数吧。我们可以随便点进去一个Span,然后点击其父类,最后查看该父类的实现方式,发现总共有12个Span类型,当然它们可能还会有子类。这些个Span具体代表什么意思呢,接下来笔者会将最常用的Span一一道来。
常用Span类型一览
1.设置字体颜色
ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(Color.parseColor("#FF0000"));
mSSB.setSpan(foregroundColorSpan,0,1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mTv.setText(mSSB);
2.设置背景颜色
BackgroundColorSpan backgroundColorSpan = new BackgroundColorSpan(Color.parseColor("#00FF00"));
mSSB.setSpan(backgroundColorSpan,1,2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mTv.setText(mSSB);
3.设置模糊效果
MaskFilter filter=new BlurMaskFilter(4.0f,BlurMaskFilter.Blur.OUTER);
MaskFilterSpan maskFilterSpan=new MaskFilterSpan(filter);
mSSB.setSpan(maskFilterSpan,2,5,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mTv.setText(mSSB);
4.设置点击事件
ClickableSpan clickableSpan = new ClickableSpan() {
@Override public void onClick(View view) {
Toast.makeText(getApplicationContext(),"点击了",Toast.LENGTH_SHORT).show();
}};
mSSB.setSpan(clickableSpan,5,7,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
// 设置此方法后,点击事件才能生效
mTv.setMovementMethod(LinkMovementMethod.getInstance());
mTv.setText(mSSB);
5.设置删除线
mSSB.setSpan(new StrikethroughSpan(),7,9,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mTv.setMovementMethod(LinkMovementMethod.getInstance());
mTv.setText(mSSB);
6.设置下划线
UnderlineSpan underlineSpan=new UnderlineSpan();
mSSB.setSpan(underlineSpan,9,12,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mTv.setText(mSSB);
7.设置文字大小
AbsoluteSizeSpan ab=new AbsoluteSizeSpan(36,true);
mSSB.setSpan(ab,12,14,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mTv.setText(mSSB);
8.指定位置插入图片
Drawable drawable = getResources().getDrawable(R.mipmap.ic_launcher_round);
drawable.setBounds(0,0,100,100);
ImageSpan ab = new ImageSpan(drawable);
mSSB.setSpan(ab,15,16,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mTv.setText(mSSB);
9.上下缩放效果
ScaleXSpan ab = new ScaleXSpan(3.0f);
mSSB.setSpan(ab,16,18,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mTv.setText(mSSB);
10.设置粗体斜体
StyleSpan ab = new StyleSpan(Typeface.BOLD_ITALIC);
mSSB.setSpan(ab,18,22,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mTv.setText(mSSB);
11.设置上标,例如多少次方
SuperscriptSpan ab=new SuperscriptSpan();
mSSB.setSpan(ab,22,23,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mTv.setText(mSSB);
12.设置下标,例如log(n)
SubscriptSpan ab=new SubscriptSpan();
mSSB.setSpan(ab,23,24,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mTv.setText(mSSB);
13.设置超链接
URLSpan ab = new URLSpan ("http://www.baidu.com");
mSSB.setSpan(ab,25,27,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mTv.setMovementMethod(new LinkMovementMethod());
mTv.setText(mSSB);
14.设置字体样式
TextAppearanceSpan ab=new TextAppearanceSpan(this, android.R.style.TextAppearance_DeviceDefault_Large);
mSSB.setSpan(ab,24,25,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mTv.setText(mSSB);
15.设置字体
TypefaceSpan ab=new TypefaceSpan ("serif");
mSSB.setSpan(ab,27,28,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mTv.setText(mSSB);
坑点一览
坑点一 文本中相同的字符会默认复用原来的该字符Span样式
请看下图,我只是设置了少年,这3个字符的样式,但是实际上后面有一个逗号也复用了该样式。总结一下就是,前面设置过的样式,后面相同字符仍然生效!
解决方案是给后面的逗号重新设置新样式,就不会采用默认的了,如下图,我给它设置了一个新样式,新样式生效了。
坑点二 修改点击事件的颜色
点击事件ClickableSpan使用后,默认是有颜色的。但是它并没有提供setColor的方法,如果我们想修改它的颜色应该怎么办呢?
这里需要复写UnderlineSpan类的updateDrawState方法,在里面进行修改
mSSB.setSpan(new UnderlineSpan() {
@Override
public void updateDrawState(TextPaint ds) {
ds.setColor(Color.BLUE);//设置颜色
//ds.setUnderlineText(false);//去掉下划线
}}, 5, 7, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
坑点三 插入图片时会覆盖原内容
这是默认的效果,在插入图片时,图片是会占位置的,这里王字就被图片覆盖了。
为了解决这个问题,可以在需要插入图片的地方插入几个空格,或者回车即可,让图片去覆盖不重要的字符。
总结
SpannableStringBuilder是一个高度定制化的StringBuilder,可以动态修改一个字符串中间某一段的样式,功能非常强大。本文简单介绍了SpannableStringBuilder的作用,已经如何使用,其次列举出了所有可设置的Span,以后碰到有一个TextView里多样式的情况可以直接拿过去使用了。掌握了SpannableStringBuilder以后,其实还可以做很多的事情,比如加入一些动画可以制作一些很酷炫的文字效果。除此之外,还可以实现最最简单的图文混排,当然复杂的图文混排还是要自定义view去做了。当然还有很多,比如文本最后的全文、展开效果等等。
谢谢各位看官大大们赏脸看到了文末,最后贴一张所有样式的全家福吧,是不是感觉很强大呢!