Android UI——SpannableString詳細(xì)解析

前言

相信很多朋友在日常開發(fā)中都遇到過這樣的問題:有一段文本,需要單獨給它各部分文字設(shè)置不同的樣式办绝,有的文字設(shè)置為粗體伊约,有的文字設(shè)置特殊的顏色,有的地方要加入表情孕蝉,遇到數(shù)學(xué)公式還可能要設(shè)置上下標(biāo)屡律,這時候該怎么辦呢?

有的人可能會說:簡單降淮,不同樣式的文字就用不同的TextView超埋,這樣就可以完美解決了。先不說這個方法行不行得通骤肛,事實上纳本,若采用這種方式,當(dāng)碰上一段文字需要設(shè)置非常多的樣式時腋颠,光是這一堆TextView就夠浪費資源的了繁成,布局還復(fù)雜,也不利于維護淑玫,因此這種方式一般不會被采用巾腕。

那么有其他辦法嗎?有絮蒿,并且還很簡單尊搬,今天介紹的這個SpannableString就是用來解決這個問題的。

SpannableString

什么是SpannableString土涝?

SpannableString佛寿,是CharSequence的一種,原本的CharSequence只是一串字符序列,沒有任何樣式冀泻,而SpannableString可以在字符序列基礎(chǔ)上對指定的字符進行潤飾常侣,在開發(fā)中,TextView可以通過setText(CharSequence)傳入SpannableString作為參數(shù)弹渔,來達(dá)到顯示不同樣式文字的效果胳施。

創(chuàng)建方式

SpannableString spannableString = new SpannableString("如果我是陳奕迅");

如何對SpannableString進行潤飾?

一般通過以下方式進行設(shè)置

spannableString.setSpan(Object what, int start, int end, int flags);

這里講解一下幾個參數(shù)的意義

  • what:對SpannableString進行潤色的各種Span肢专;
  • int:需要潤色文字段開始的下標(biāo)舞肆;
  • end:需要潤色文字段結(jié)束的下標(biāo);
  • flags:決定開始和結(jié)束下標(biāo)是否包含的標(biāo)志位博杖,有四個參數(shù)可選
    • SPAN_INCLUSIVE_EXCLUSIVE:包括開始下標(biāo)椿胯,但不包括結(jié)束下標(biāo)
    • SPAN_EXCLUSIVE_INCLUSIVE:不包括開始下標(biāo),但包括結(jié)束下標(biāo)
    • SPAN_INCLUSIVE_INCLUSIVE:既包括開始下標(biāo)欧募,又包括結(jié)束下標(biāo)
    • SPAN_EXCLUSIVE_EXCLUSIVE:不包括開始下標(biāo)压状,也不包括結(jié)束下標(biāo)

這里涉及到一個重要的角色,就是各種各樣的span跟继,它決定我們要對文字的進行怎樣的潤飾种冬,而后三個參數(shù)決定潤飾哪些文字,為了方便起見舔糖,后面的flags默認(rèn)都使用SPAN_INCLUSIVE_EXCLUSIVE模式娱两。

各種Span

先來看一張類結(jié)構(gòu)圖,了解各種Span之間的關(guān)系

Span類結(jié)構(gòu)圖

可以看出所有Span都繼承于CharacterStyle這個抽象類金吗,另外MetricAffectingSpan十兢、ReplacementSpan和ClickableSpan都是抽象類,下面展示一些常用的Span

ForegroundColorSpan

代碼

SpannableString spannableString = new SpannableString("如果我是陳奕迅");
ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(Color.GREEN);
spannableString.setSpan(foregroundColorSpan, 4, 7, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
mTextView.setText(spannableString);

ForegroundColorSpan:前景色摇庙,也就是對文字上色旱物,顏色設(shè)置為GREEN,start為4卫袒,end為7宵呛,應(yīng)該是“陳奕迅”三個字顯示為綠色,看一下實際效果

ForegroundColorSpan

BackgroudColorSpan

代碼

SpannableString spannableString = new SpannableString("如果我是陳奕迅");
BackgroundColorSpan backgroundColorSpan = new BackgroundColorSpan(Color.GREEN);
spannableString.setSpan(backgroundColorSpan, 4, 7, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
mTextView.setText(spannableString);

BackgroudColorSpan:與ForegroundColorSpan類似夕凝,對文字背景上色

BackgroudColorSpan

ClickableSpan

代碼

SpannableString spannableString = new SpannableString("如果我是陳奕迅");
ClickableSpan clickableSpan = new ClickableSpan() {
    @Override
    public void onClick(View widget) {
        Toast.makeText(MainActivity.this, "如果我是陳奕迅", Toast.LENGTH_SHORT).show();
    }
    @Override
    public void updateDrawState(TextPaint ds) {
        ds.setUnderlineText(false);
    }
};
spannableString.setSpan(clickableSpan, 4, 7, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
mTextView.setMovementMethod(LinkMovementMethod.getInstance());
mTextView.setText(spannableString);

ClickableSpan:是一個抽象類宝穗,實現(xiàn)可點擊效果,可以重寫onClick方法實現(xiàn)點擊事件码秉,這里點擊“陳奕迅”三個字簡單地彈toast

ClickableSpan

URLSpan

代碼

SpannableString spannableString = new SpannableString("如果我是陳奕迅");
URLSpan urlSpan = new URLSpan("https://www.baidu.com/s?ie=UTF-8&wd=陳奕迅");
spannableString.setSpan(urlSpan, 4, 7, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
mTextView.setMovementMethod(LinkMovementMethod.getInstance());
mTextView.setText(spannableString);

URLSpan:實現(xiàn)超鏈接的效果逮矛,繼承于ClickableSpan,點擊實現(xiàn)跳轉(zhuǎn)到瀏覽器

URLSpan

MaskFilterSpan

代碼

SpannableString spannableString = new SpannableString("如果我是陳奕迅");
MaskFilterSpan embossMaskFilterSpan =
    new MaskFilterSpan(new EmbossMaskFilter(new float[]{10, 10, 10}, 0.5f, 1, 1));
spannableString.setSpan(embossMaskFilterSpan, 0, 4, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
RelativeSizeSpan relativeSizeSpan = new RelativeSizeSpan(1.5f);
spannableString.setSpan(relativeSizeSpan, 0, 4, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
MaskFilterSpan blurMaskFilterSpan = new MaskFilterSpan(new BlurMaskFilter(10, Blur.NORMAL));
spannableString.setSpan(blurMaskFilterSpan, 4, 7, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
mTextView.setText(spannableString);

MaskFilterSpan:構(gòu)造方法接受MaskFilter作為參數(shù)转砖,其中它有兩個子類:EmbossMaskFilter和BlurMaskFilter
EmbossMaskFilter實現(xiàn)浮雕效果

EmbossMaskFilter(float[] direction, float ambient, float specular, float blurRadius)
  • direction:float數(shù)組须鼎,定義長度為3的數(shù)組標(biāo)量[x,y,z],來指定光源的方向
  • ambient:環(huán)境光亮度,0~1
  • specular:鏡面反射系數(shù)
  • blurRadius:模糊半徑晋控,必須>0

BlurMaskFilter實現(xiàn)模糊效果

BlurMaskFilter(float radius, Blur style)
  • radius:模糊半徑
  • style:有四個參數(shù)可選
    • BlurMaskFilter.Blur.NORMAL:內(nèi)外模糊
    • BlurMaskFilter.Blur.OUTER:外部模糊
    • BlurMaskFilter.Blur.INNER:內(nèi)部模糊
    • BlurMaskFilter.Blur.SOLID:內(nèi)部加粗挑围,外部模糊
MaskFilterSpan

RelativeSizeSpan

代碼

SpannableString spannableString = new SpannableString("如果我是陳奕迅");
RelativeSizeSpan relativeSizeSpan = new RelativeSizeSpan(1.5f);
spannableString.setSpan(relativeSizeSpan, 4, 7, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
mTextView.setText(spannableString);

RelativeSizeSpan:設(shè)置字體的相對大小,這里設(shè)置為TextView大小的1.5倍糖荒,看圖

RelativeSizeSpan

AbsoluteSizeSpan

代碼

SpannableString spannableString = new SpannableString("如果我是陳奕迅");
AbsoluteSizeSpan absoluteSizeSpan = new AbsoluteSizeSpan(40, true);
spannableString.setSpan(absoluteSizeSpan, 4, 7, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
mTextView.setText(spannableString);

AbsoluteSizeSpan:設(shè)置字體的相絕對大小,40表示文字大小模捂,true表示單位為dip捶朵,若為false則表示px

AbsoluteSizeSpan

ScaleXSpan

代碼

SpannableString spannableString = new SpannableString("如果我是陳奕迅");
ScaleXSpan scaleXSpan= new ScaleXSpan(1.5f);
spannableString.setSpan(scaleXSpan, 4, 7, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
mTextView.setText(spannableString);

ScaleXSpan:設(shè)置字體x軸縮放,1.5表示x軸放大為1.5倍狂男,效果如圖

ScaleXSpan

StyleSpan

代碼

SpannableString spannableString = new SpannableString("如果我是陳奕迅");
StyleSpan boldSpan = new StyleSpan(Typeface.BOLD);
StyleSpan italicSpan = new StyleSpan(Typeface.ITALIC);
StyleSpan boldItalicSpan = new StyleSpan(Typeface.BOLD_ITALIC);
spannableString.setSpan(boldSpan, 0, 2, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(italicSpan, 2, 4, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(boldItalicSpan, 4, 7, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
mTextView.setText(spannableString);

StyleSpan:設(shè)置文字樣式综看,如斜體、粗體

StyleSpan

TypefaceSpan

代碼

SpannableString spannableString = new SpannableString("如果我是陳奕迅");
TypefaceSpan monospace = new TypefaceSpan("monospace");
TypefaceSpan serif = new TypefaceSpan("serif");
TypefaceSpan sans_serif = new TypefaceSpan("sans-serif");
spannableString.setSpan(monospace, 0, 2, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(serif, 2, 4, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(sans_serif, 4, 7, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
mTextView.setText(spannableString);

TypefaceSpan:設(shè)置文字字體類型岖食,如monospace红碑、serif和sans-serif等等

TypefaceSpan

TextAppearanceSpan

代碼

SpannableString spannableString = new SpannableString("如果我是陳奕迅");
TextAppearanceSpan textAppearanceSpan = new TextAppearanceSpan(this, android.R.style.TextAppearance_Material);
spannableString.setSpan(textAppearanceSpan, 4, 7, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
mTextView.setText(spannableString);

TextAppearanceSpan:設(shè)置文字外貌,通過style資源設(shè)置泡垃,這里使用系統(tǒng)的style資源

TextAppearanceSpan

UnderlineSpan

代碼

SpannableString spannableString = new SpannableString("如果我是陳奕迅");
UnderlineSpan underlineSpan = new UnderlineSpan();
spannableString.setSpan(underlineSpan, 4, 7, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
mTextView.setText(spannableString);

UnderlineSpan:設(shè)置文字下劃線析珊,強調(diào)突出文字時可以使用該span

UnderlineSpan

StrikethroughSpan

代碼

SpannableString spannableString = new SpannableString("如果我是陳奕迅");
StrikethroughSpan strikethroughSpan = new StrikethroughSpan();
spannableString.setSpan(strikethroughSpan, 4, 7, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
mTextView.setText(spannableString);

StrikethroughSpan:設(shè)置文字刪除線

StrikethroughSpan

SuperscriptSpan

代碼

SpannableString spannableString = new SpannableString("如果我是陳奕迅");
SuperscriptSpan superscriptSpan = new SuperscriptSpan();
RelativeSizeSpan relativeSizeSpan = new RelativeSizeSpan(0.8f);
spannableString.setSpan(relativeSizeSpan, 4, 7, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(superscriptSpan, 4, 7, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
mTextView.setText(spannableString);

SuperscriptSpan:設(shè)置文字為上標(biāo)

SuperscriptSpan

SubscriptSpan

代碼

SpannableString spannableString = new SpannableString("如果我是陳奕迅");
SubscriptSpan subscriptSpan = new SubscriptSpan();
RelativeSizeSpan relativeSizeSpan = new RelativeSizeSpan(0.8f);
spannableString.setSpan(relativeSizeSpan, 4, 7, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(subscriptSpan, 4, 7, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
mTextView.setText(spannableString);

SubscriptSpan:設(shè)置文字為下標(biāo)

SubscriptSpan

ImageSpan

代碼

SpannableString spannableString = new SpannableString("如果我是陳奕迅");
ImageSpan imageSpan = new ImageSpan(this, R.drawable.ic_eason);
spannableString.setSpan(imageSpan, 4, 7, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
mTextView.setText(spannableString);

ImageSpan:設(shè)置圖片

ImageSpan

總結(jié)

總結(jié)一下以上提到的Span

  • ForegroundColorSpan:前景色
  • BackgroundColorSpan:背景色
  • ClickableSpan:抽象類,可點擊效果蔑穴,重寫onClick方法響應(yīng)點擊事件
  • URLSpan:超鏈接
  • MaskFilterSpan:EmbossMaskFilter浮雕效果忠寻,BlurMaskFilter模糊效果
  • RelativeSpan:文字相對大小
  • AbsoluteSpan:文字絕對大小
  • ScaleXSpan:x軸縮放
  • styleSpan:文字樣式
  • TypefaceSpan:文字字體類型
  • TextApearanceSpan:文字外貌
  • UnderlineSpan:下劃線
  • StrikeThroughSpan:刪除線
  • SuperscriptSpan:上標(biāo)
  • SubscriptSpan:下標(biāo)
  • ImageSpan:圖片

這些Span能夠很好地幫助我們潤色文字,以非常簡單地方式獲得復(fù)雜和絢麗的文字效果存和,著實是開發(fā)中的一大利器奕剃,喜歡的朋友收藏備用吧

感謝閱讀!

歡迎關(guān)注個人微信公眾號:Charming寫字的地方

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末捐腿,一起剝皮案震驚了整個濱河市纵朋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌茄袖,老刑警劉巖操软,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異绞佩,居然都是意外死亡寺鸥,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門品山,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胆建,“玉大人,你說我怎么就攤上這事肘交“试兀” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長凉驻。 經(jīng)常有香客問我腻要,道長,這世上最難降的妖魔是什么涝登? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任雄家,我火速辦了婚禮,結(jié)果婚禮上胀滚,老公的妹妹穿的比我還像新娘趟济。我一直安慰自己,他們只是感情好咽笼,可當(dāng)我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布顷编。 她就那樣靜靜地躺著,像睡著了一般剑刑。 火紅的嫁衣襯著肌膚如雪媳纬。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天施掏,我揣著相機與錄音钮惠,去河邊找鬼。 笑死七芭,一個胖子當(dāng)著我的面吹牛萌腿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播抖苦,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼贪绘,長吁一口氣:“原來是場噩夢啊……” “哼跷坝!你這毒婦竟也來了欺冀?” 一聲冷哼從身側(cè)響起蹄葱,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎究西,沒想到半個月后窗慎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡卤材,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年遮斥,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扇丛。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡术吗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出帆精,到底是詐尸還是另有隱情较屿,我是刑警寧澤隧魄,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站隘蝎,受9級特大地震影響购啄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嘱么,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一狮含、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧曼振,春花似錦辉川、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽府蛇。三九已至集索,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間汇跨,已是汗流浹背务荆。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留穷遂,地道東北人函匕。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像蚪黑,于是被迫代替她去往敵國和親盅惜。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,792評論 2 345

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