Android SpannableString和SpannableStringBuilder教程

一.导引

1.适用对象

  • 没有接触过SpannableString的人

  • 听过但是不熟悉不了解SpannableString的人。

2.教程结构

  • 简介
  • SpannableString
  • SpannableStringBuilder
  • 实战部分
  • 总结和感想(作者瞎逼逼时间)

二.正文

1.简介

SpannableString和SpannableStringBuilder的关系类似于String和StringBuilder。前者不可变,后者可变。所以两者的使用方法基本相同。

功能在于给一串普通的字符串加上颜色,大小背景等样式和特殊事件(点击事件)。下面先上一个例子

image

这个例子很普通,小伙伴们一看可能会觉得这不就是几个TextView吗?

没错,这就是TextView。但不是几个,而是一个,只用一个TextView显示出这串花花绿绿的文字是不是很🐂的感觉呢?

这个时候可能有小伙伴会说:“一个TextView?也简单啊,我写成html,加点color,background样式,然后用 Html.fromHtml( ) 一下还不是轻轻松松,要你这破Span啥啥的干啥用!”

没错,这样子是能实现这个效果,但是你们不觉得一串串html的代码硬编码在Android项目里很难看么!而且经过我对 Html.fromHtml( ) 的源码的研究发现,这个方法并没有什么神秘之处(能够DuangDuang的就给文字加特技,呸! 加样式)。

mSpannableStringBuilder = new SpannableStringBuilder();
if (end == start) {
                mSpannableStringBuilder.removeSpan(obj[i]);
            } else {
                mSpannableStringBuilder.setSpan(obj[i], start, end, Spannable.SPAN_PARAGRAPH);
            }

上面是截取了部分Html.fromHtml( ) 方法调用的源码。细心的小伙伴们肯定发现了,这里面出现了个SpannableStringBuilder()。所以一切真相大白了,原来该方法将html解析之后通过SpannableStringBuilder来给他添加样式。看到这的小伙伴们是不是学会用SpannableStringBuilder很有用了呢!😁

2.SpannableString
        SpannableString ss=new SpannableString("这是另外一串普通的文字");
        ForegroundColorSpan colorSpan=new ForegroundColorSpan(Color.RED);
        ss.setSpan(colorSpan,0,5,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        tv.setText(ss);

使用方法和SpannableStringBuilder类似,简单举个例子,不做过多的解释。

3.SpannableStringBuilder

本章的主角,SpannableString的好基友。

class SpannableStringBuilder implements CharSequence, GetChars, Spannable, Editable,
        Appendable, GraphicsOperations{}
//这里主要注意CharSequence和Spannable
//继承自Spannable,赋予了它给文本设定样式的基础功能
//实现接口CharSequence则代表了他能在很多地方使用,比如TextView的setText方法的参数就是CharSequence对象

下面来看主要的两个方法

    public SpannableStringBuilder append(CharSequence text) {}
    //将文本添加到SpannableStringBuilder中,和StringBuilder的append方法功能类似
    public void setSpan(Object what, int start, int end, int flags) {}

用于设置样式的核心方法。参数:

  1. what
    各种Span,不同的Span对应不同的样式,具体有如下:
  • ForegroundColorSpan : 设置文本前景色(文本颜色)
  • BackgroundColorSpan : 设置背景颜色
  • AbsoluteSizeSpan : 设置绝对的文字大小,px单位
  • ClickableSpan : 为文字添加点击事件(类似于微信朋友圈评论列表中用户的昵称点击事件就可以用这个实现)
  • DynamicDrawableSpan :
  • ImageSpan : 文文本添加图片
  • RelativeSizeSpan : 设置相对文字大小,为倍数,相对于其他文字的大小
  • StrikethroughSpan : 添加删除线
  • SubscriptSpan : 设置下标文字
  • SuperscriptSpan : 设置上标文字
  • URLSpan : 文字设定超链接
  • UnderlineSpan : 设置下划线
  1. start
    样式生效的开始位置,包括该位置
    3)end
    样式结束的位置,不包括该位置,所以设定一串文字中前3个文字的样式时start:0,end:3。而不是end:2
  2. flags
    这几个参数中最难懂最麻烦最难搞的一个参数。
    主要有以下四个值:
  • Spannable.SPAN_EXCLUSIVE_INCLUSIVE:在 Span前面输入的字符不应用 span 的效果,在后面输入的字符应用Span效果。
  • Spannable.SPAN_INCLUSIVE_EXCLUSIVE:在 Span前面输入的字符应用 span 的效果,在后面输入的字符不应用Span效果。
  • Spannable.SPAN_INCUJSIVE_INCLUSIVE:在 Span前后输入的字符都应用 span 的效果。
  • Spannable.SPAN_EXCLUSIVE_EXCLUSIVE:在 Span前后输入的字符前后都不应用 span 的效果。
    看得小伙伴们一头雾水对吧,前前后后用不用的,啥玩意?
    接下来我上两张图让大家看懂这四个flag的区别


    image

    这是对同一段文字设置相同的span,区别在于flag的不同,4个的效果是不是一毛一样呢?


    image

    这张图片中的 “zz” 是在EditText中输入进去的,小伙伴们结合上面对4个flag的介绍,是不是能理解了呢?
        //部分代码
        final String baseString="这是开始的文字";
        SpannableStringBuilder sb;
        ForegroundColorSpan span;

        sb=new SpannableStringBuilder();
        sb.append(baseString);
        span=new ForegroundColorSpan(Color.RED);
        sb.setSpan(span,2,4,Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
        et.setText(sb);

        sb=new SpannableStringBuilder();
        sb.append(baseString);
        span=new ForegroundColorSpan(Color.RED);
        sb.setSpan(span,2,4,Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
        et2.setText(sb);

        sb=new SpannableStringBuilder();
        sb.append(baseString);
        span=new ForegroundColorSpan(Color.RED);
        sb.setSpan(span,2,4,Spanned.SPAN_INCLUSIVE_INCLUSIVE);
        et3.setText(sb);

        sb=new SpannableStringBuilder();
        sb.append(baseString);
        span=new ForegroundColorSpan(Color.RED);
        sb.setSpan(span,2,4,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        et4.setText(sb);
4.激动人心的实战时刻

ForegroundColorSpan的使用:

        SpannableStringBuilder sb=new SpannableStringBuilder();
        sb.append("红色绿色蓝色");
        ForegroundColorSpan colorSpan=new ForegroundColorSpan(Color.RED);
        sb.setSpan(colorSpan,0,2,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

        colorSpan=new ForegroundColorSpan(Color.GREEN);
        sb.setSpan(colorSpan,2,4,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

        colorSpan=new ForegroundColorSpan(Color.BLUE);
        sb.setSpan(colorSpan,4,6,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

        tv.setText(sb);

效果:


image

花花绿绿的最好看了☺

ImageSpan:

        SpannableStringBuilder sb=new SpannableStringBuilder();
        sb.append("图片前面的变成图片了");
        ImageSpan span=new ImageSpan(this,R.mipmap.ic_launcher);
        sb.setSpan(span,0,2,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        //替换掉了前两个文字,所以添加的图片占用两个文字的宽度
        tv.setText(sb);

效果:


image

说好的大家一起做文字的,你却偷偷整容成图片了,哼!

ClickableSpan:

        SpannableStringBuilder sb=new SpannableStringBuilder();
        sb.append("我们中有两个文字可以点击哦");
        ClickableSpan span=new ClickableSpan() {
            @Override
            public void onClick(View widget) {
                Toast.makeText(MainActivity.this,"你点击了我",Toast.LENGTH_LONG).show();
            }
        };
        sb.setSpan(span,0,2,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        tv.setText(sb);

        //设置了点击事件后请加上这句,不然点击事件不起作用
        tv.setMovementMethod(LinkMovementMethod.getInstance());

效果:


image

别忘了这一句:tv.setMovementMethod(LinkMovementMethod.getInstance());
很重要。

另外给文字设置点击事件之后会自动给文字加上下划线,可以使用如下方式去除这个默认的下划线:

    //自定义类继承自ClickableSpan。用该类来代替使用
    public abstract class NoLineClickSpan extends ClickableSpan {
        public NoLineClickSpan() {
            super();
        }
        @Override
        public void updateDrawState(TextPaint ds) {
            /**set textColor**/
            ds.setColor(ds.linkColor);
            /**Remove the underline**/
            ds.setUnderlineText(false);
        }
        @Override
        public abstract void onClick(View widget);
    }

由于篇幅所限,在此就介绍这么多的Span使用例子,其他几个Span的适用方法还请小伙伴们自己探索,毕竟自己动手学的快嘛(我才不会说是我自己懒😕!)
另外小伙伴们是不是觉得new出一个又一个的span很麻烦呢,而且手动计算start,end很麻烦呢?下面给大家推荐一个简单的辅助类。

好了,以上都是广告时间,下面进入正文:
欢迎小伙伴使用我写的一个辅助工具类,gayhub地址:https://github.com/zYinux/SpecialString
使用了该库之后设置样式只需要:

 //构建SpecialStyle 用来设置样式的核心类
        SpecialStyle style=new SpecialStyle();
        SpecialStringBuilder sb=new SpecialStringBuilder();

        //设置文本颜色为黑色。第二个参数save的意思是代表该样式是否应用到下一段文字,如果不传则为true
        style.setColor(Color.BLACK,false);
        //为文字设置样式
        sb.append("售价:",style);

        style.setColor(Color.RED,false);
        sb.append("¥99.99  ",style);

        //设置颜色背景和点击事件样式
        //点击事件默认为不应用于下一段文字
        style.setColor(Color.GREEN,false)
             .setBackgroundColor(Color.rgb(200,200,200),false)
                .setClickable(new ClickableStyle.OnClick() {
            @Override
            public void onClick(View widget) {
                Toast.makeText(MainActivity.this,"开始抢购",Toast.LENGTH_SHORT).show();
            }
        });
        sb.append("立即抢购",style);

        //为TextView设置刚刚构建的文本
        tv.setText(sb.getCharSequence());
        //如果为文字添加了点击事件,请添加这一句,否则点击事件不生效
        tv.setMovementMethod(LinkMovementMethod.getInstance());

是不是简单了很多了呢?终于不用去理会那些烦人的start,end,flag了。具体使用方法请大家转到GitHub查看,方便的话欢迎小伙伴给个star,谢谢啦。

5.瞎逼逼时间

小伙伴们好!
我叫 zYinux ,取这个网名是为了致敬IT界的大佬的项目Linux。可惜目前我还是一个刚入门的菜鸟,希望能通过自己的努力攀爬到更高的境界。这是我写的第二篇博客,上一篇已是1年前了,接下来会努力一个月出一篇博客。文笔不好望大家见谅,希望看到这里的小伙伴已经学会了上面的知识,如果小伙伴发现了教程中有错误和遗漏之处,欢迎联系我指正。
QQ交流群:589184413

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容