什么,同一个文本可以设置多种样式?

2,785

       不知道大家在工作中是否遇到过这种情况,明明就是一个字符串,但是设计师偏让我们显示成不同的样式,具体效果和下图类似


        如果你用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去做了。当然还有很多,比如文本最后的全文、展开效果等等。

       谢谢各位看官大大们赏脸看到了文末,最后贴一张所有样式的全家福吧,是不是感觉很强大呢!