Android SpannableString和SpannableStringBuilder教程

一.導(dǎo)引

1.適用對(duì)象

  • 沒(méi)有接觸過(guò)SpannableString的人

  • 聽(tīng)過(guò)但是不熟悉不了解SpannableString的人吵冒。

2.教程結(jié)構(gòu)

  • 簡(jiǎn)介
  • SpannableString
  • SpannableStringBuilder
  • 實(shí)戰(zhàn)部分
  • 總結(jié)和感想(作者瞎逼逼時(shí)間)

二.正文

1.簡(jiǎn)介

SpannableString和SpannableStringBuilder的關(guān)系類(lèi)似于String和StringBuilder概荷。前者不可變,后者可變衷畦。所以?xún)烧叩氖褂梅椒ɑ鞠嗤?/p>

功能在于給一串普通的字符串加上顏色竹海,大小背景等樣式和特殊事件(點(diǎn)擊事件)晰骑。下面先上一個(gè)例子

image

這個(gè)例子很普通敛劝,小伙伴們一看可能會(huì)覺(jué)得這不就是幾個(gè)TextView嗎?

沒(méi)錯(cuò)纷宇,這就是TextView夸盟。但不是幾個(gè),而是一個(gè)像捶,只用一個(gè)TextView顯示出這串花花綠綠的文字是不是很??的感覺(jué)呢上陕?

這個(gè)時(shí)候可能有小伙伴會(huì)說(shuō):“一個(gè)TextView?也簡(jiǎn)單啊拓春,我寫(xiě)成html释簿,加點(diǎn)color,background樣式硼莽,然后用 Html.fromHtml( ) 一下還不是輕輕松松庶溶,要你這破Span啥啥的干啥用!”

沒(méi)錯(cuò),這樣子是能實(shí)現(xiàn)這個(gè)效果懂鸵,但是你們不覺(jué)得一串串html的代碼硬編碼在Android項(xiàng)目里很難看么偏螺!而且經(jīng)過(guò)我對(duì) Html.fromHtml( ) 的源碼的研究發(fā)現(xiàn),這個(gè)方法并沒(méi)有什么神秘之處(能夠DuangDuang的就給文字加特技匆光,呸! 加樣式)套像。

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

上面是截取了部分Html.fromHtml( ) 方法調(diào)用的源碼。細(xì)心的小伙伴們肯定發(fā)現(xiàn)了终息,這里面出現(xiàn)了個(gè)SpannableStringBuilder()夺巩。所以一切真相大白了贞让,原來(lái)該方法將html解析之后通過(guò)SpannableStringBuilder來(lái)給他添加樣式×看到這的小伙伴們是不是學(xué)會(huì)用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類(lèi)似,簡(jiǎn)單舉個(gè)例子征绎,不做過(guò)多的解釋蹲姐。

3.SpannableStringBuilder

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

class SpannableStringBuilder implements CharSequence, GetChars, Spannable, Editable,
        Appendable, GraphicsOperations{}
//這里主要注意CharSequence和Spannable
//繼承自Spannable柴墩,賦予了它給文本設(shè)定樣式的基礎(chǔ)功能
//實(shí)現(xiàn)接口CharSequence則代表了他能在很多地方使用,比如TextView的setText方法的參數(shù)就是CharSequence對(duì)象

下面來(lái)看主要的兩個(gè)方法

    public SpannableStringBuilder append(CharSequence text) {}
    //將文本添加到SpannableStringBuilder中凫岖,和StringBuilder的append方法功能類(lèi)似
    public void setSpan(Object what, int start, int end, int flags) {}

用于設(shè)置樣式的核心方法江咳。參數(shù):

  1. what
    各種Span,不同的Span對(duì)應(yīng)不同的樣式哥放,具體有如下:
  • ForegroundColorSpan : 設(shè)置文本前景色(文本顏色)
  • BackgroundColorSpan : 設(shè)置背景顏色
  • AbsoluteSizeSpan : 設(shè)置絕對(duì)的文字大小歼指,px單位
  • ClickableSpan : 為文字添加點(diǎn)擊事件(類(lèi)似于微信朋友圈評(píng)論列表中用戶(hù)的昵稱(chēng)點(diǎn)擊事件就可以用這個(gè)實(shí)現(xiàn))
  • DynamicDrawableSpan :
  • ImageSpan : 文文本添加圖片
  • RelativeSizeSpan : 設(shè)置相對(duì)文字大小,為倍數(shù)甥雕,相對(duì)于其他文字的大小
  • StrikethroughSpan : 添加刪除線(xiàn)
  • SubscriptSpan : 設(shè)置下標(biāo)文字
  • SuperscriptSpan : 設(shè)置上標(biāo)文字
  • URLSpan : 文字設(shè)定超鏈接
  • UnderlineSpan : 設(shè)置下劃線(xiàn)
  1. start
    樣式生效的開(kāi)始位置踩身,包括該位置
    3)end
    樣式結(jié)束的位置,不包括該位置社露,所以設(shè)定一串文字中前3個(gè)文字的樣式時(shí)start:0,end:3挟阻。而不是end:2
  2. flags
    這幾個(gè)參數(shù)中最難懂最麻煩最難搞的一個(gè)參數(shù)。
    主要有以下四個(gè)值:
  • Spannable.SPAN_EXCLUSIVE_INCLUSIVE:在 Span前面輸入的字符不應(yīng)用 span 的效果峭弟,在后面輸入的字符應(yīng)用Span效果附鸽。
  • Spannable.SPAN_INCLUSIVE_EXCLUSIVE:在 Span前面輸入的字符應(yīng)用 span 的效果,在后面輸入的字符不應(yīng)用Span效果瞒瘸。
  • Spannable.SPAN_INCUJSIVE_INCLUSIVE:在 Span前后輸入的字符都應(yīng)用 span 的效果坷备。
  • Spannable.SPAN_EXCLUSIVE_EXCLUSIVE:在 Span前后輸入的字符前后都不應(yīng)用 span 的效果。
    看得小伙伴們一頭霧水對(duì)吧情臭,前前后后用不用的省撑,啥玩意?
    接下來(lái)我上兩張圖讓大家看懂這四個(gè)flag的區(qū)別


    image

    這是對(duì)同一段文字設(shè)置相同的span俯在,區(qū)別在于flag的不同丁侄,4個(gè)的效果是不是一毛一樣呢?


    image

    這張圖片中的 “zz” 是在EditText中輸入進(jìn)去的朝巫,小伙伴們結(jié)合上面對(duì)4個(gè)flag的介紹鸿摇,是不是能理解了呢?
        //部分代碼
        final String baseString="這是開(kāi)始的文字";
        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.激動(dòng)人心的實(shí)戰(zhàn)時(shí)刻

ForegroundColorSpan的使用:

        SpannableStringBuilder sb=new SpannableStringBuilder();
        sb.append("紅色綠色藍(lán)色");
        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);
        //替換掉了前兩個(gè)文字劈猿,所以添加的圖片占用兩個(gè)文字的寬度
        tv.setText(sb);

效果:


image

說(shuō)好的大家一起做文字的拙吉,你卻偷偷整容成圖片了潮孽,哼!

ClickableSpan:

        SpannableStringBuilder sb=new SpannableStringBuilder();
        sb.append("我們中有兩個(gè)文字可以點(diǎn)擊哦");
        ClickableSpan span=new ClickableSpan() {
            @Override
            public void onClick(View widget) {
                Toast.makeText(MainActivity.this,"你點(diǎn)擊了我",Toast.LENGTH_LONG).show();
            }
        };
        sb.setSpan(span,0,2,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        tv.setText(sb);

        //設(shè)置了點(diǎn)擊事件后請(qǐng)加上這句筷黔,不然點(diǎn)擊事件不起作用
        tv.setMovementMethod(LinkMovementMethod.getInstance());

效果:


image

別忘了這一句:tv.setMovementMethod(LinkMovementMethod.getInstance());
很重要往史。

另外給文字設(shè)置點(diǎn)擊事件之后會(huì)自動(dòng)給文字加上下劃線(xiàn),可以使用如下方式去除這個(gè)默認(rèn)的下劃線(xiàn):

    //自定義類(lèi)繼承自ClickableSpan佛舱。用該類(lèi)來(lái)代替使用
    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使用例子,其他幾個(gè)Span的適用方法還請(qǐng)小伙伴們自己探索请祖,畢竟自己動(dòng)手學(xué)的快嘛(我才不會(huì)說(shuō)是我自己懶??6┩帷)
另外小伙伴們是不是覺(jué)得new出一個(gè)又一個(gè)的span很麻煩呢,而且手動(dòng)計(jì)算start肆捕,end很麻煩呢刷晋?下面給大家推薦一個(gè)簡(jiǎn)單的輔助類(lèi)。

好了慎陵,以上都是廣告時(shí)間眼虱,下面進(jìn)入正文:
歡迎小伙伴使用我寫(xiě)的一個(gè)輔助工具類(lèi),gayhub地址:https://github.com/zYinux/SpecialString席纽。
使用了該庫(kù)之后設(shè)置樣式只需要:

 //構(gòu)建SpecialStyle 用來(lái)設(shè)置樣式的核心類(lèi)
        SpecialStyle style=new SpecialStyle();
        SpecialStringBuilder sb=new SpecialStringBuilder();

        //設(shè)置文本顏色為黑色捏悬。第二個(gè)參數(shù)save的意思是代表該樣式是否應(yīng)用到下一段文字,如果不傳則為true
        style.setColor(Color.BLACK,false);
        //為文字設(shè)置樣式
        sb.append("售價(jià):",style);

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

        //設(shè)置顏色背景和點(diǎn)擊事件樣式
        //點(diǎn)擊事件默認(rèn)為不應(yīng)用于下一段文字
        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,"開(kāi)始搶購(gòu)",Toast.LENGTH_SHORT).show();
            }
        });
        sb.append("立即搶購(gòu)",style);

        //為T(mén)extView設(shè)置剛剛構(gòu)建的文本
        tv.setText(sb.getCharSequence());
        //如果為文字添加了點(diǎn)擊事件润梯,請(qǐng)?zhí)砑舆@一句过牙,否則點(diǎn)擊事件不生效
        tv.setMovementMethod(LinkMovementMethod.getInstance());

是不是簡(jiǎn)單了很多了呢?終于不用去理會(huì)那些煩人的start仆救,end抒和,flag了矫渔。具體使用方法請(qǐng)大家轉(zhuǎn)到GitHub查看彤蔽,方便的話(huà)歡迎小伙伴給個(gè)star,謝謝啦庙洼。

5.瞎逼逼時(shí)間

小伙伴們好顿痪!
我叫 zYinux ,取這個(gè)網(wǎng)名是為了致敬IT界的大佬的項(xiàng)目Linux油够∫舷可惜目前我還是一個(gè)剛?cè)腴T(mén)的菜鳥(niǎo),希望能通過(guò)自己的努力攀爬到更高的境界石咬。這是我寫(xiě)的第二篇博客揩悄,上一篇已是1年前了,接下來(lái)會(huì)努力一個(gè)月出一篇博客鬼悠。文筆不好望大家見(jiàn)諒删性,希望看到這里的小伙伴已經(jīng)學(xué)會(huì)了上面的知識(shí)亏娜,如果小伙伴發(fā)現(xiàn)了教程中有錯(cuò)誤和遺漏之處,歡迎聯(lián)系我指正蹬挺。
QQ交流群:589184413

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末维贺,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子巴帮,更是在濱河造成了極大的恐慌溯泣,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件榕茧,死亡現(xiàn)場(chǎng)離奇詭異垃沦,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)雪猪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)栏尚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人只恨,你說(shuō)我怎么就攤上這事译仗。” “怎么了官觅?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵纵菌,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我休涤,道長(zhǎng)咱圆,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任功氨,我火速辦了婚禮序苏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘捷凄。我一直安慰自己忱详,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布跺涤。 她就那樣靜靜地躺著匈睁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪桶错。 梳的紋絲不亂的頭發(fā)上航唆,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音院刁,去河邊找鬼糯钙。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的任岸。 我是一名探鬼主播鸳玩,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼演闭!你這毒婦竟也來(lái)了不跟?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤米碰,失蹤者是張志新(化名)和其女友劉穎窝革,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體吕座,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡虐译,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吴趴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片漆诽。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖锣枝,靈堂內(nèi)的尸體忽然破棺而出厢拭,到底是詐尸還是另有隱情,我是刑警寧澤撇叁,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布供鸠,位于F島的核電站,受9級(jí)特大地震影響陨闹,放射性物質(zhì)發(fā)生泄漏楞捂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一趋厉、第九天 我趴在偏房一處隱蔽的房頂上張望寨闹。 院中可真熱鬧,春花似錦君账、人聲如沸繁堡。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)帖蔓。三九已至矮瘟,卻和暖如春瞳脓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背澈侠。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工劫侧, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓烧栋,卻偏偏與公主長(zhǎng)得像写妥,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子审姓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容