SpannableString实现富文本样式

  • 使用下面两个类的setSpan()方法设置不同的样式Span来实现富文本:

SpannableString - 实例化对象时必须传入一个字符串String,之后内容无法改变
SpannableStringBuilder - 实例化对象时可不传入一个String字符串,而用append()来添加内容,可动态拼接多个字符串String

  • setSpan(Object what, int start, int end, int flags);

函数说明:
给SpannableString或SpannableStringBuilder指定范围内的字符串设定Span样式,可以设置多个Span样式,flags参数标识了当在所标记范围前和标记范围后紧贴着插入新字符时的动作,即是否对新插入的字符应用同样的样式。

参数说明:
Object what:对应的各种Span样式
int start:开始应用指定Span的位置,索引从0开始
int end:结束应用指定Span的位置,作用范围为[start, end)
int flags:

Spanned.SPAN_EXCLUSIVE_EXCLUSIVE:前后都不包括。即在指定范围的前面和后面插入新字符都不会应用新样式 
Spanned.SPAN_EXCLUSIVE_INCLUSIVE:前面不包括,后面包括。即仅在指定范围字符的后面插入新字符时会应用新样式
Spanned.SPAN_INCLUSIVE_EXCLUSIVE:前面包括,后面不包括。即仅在指定范围字符的前面插入新字符时会应用新样式
Spanned.SPAN_INCLUSIVE_INCLUSIVE:前后都包括。即在指定范围字符的前面和后面插入新字符时都会应用新样式

Linkify - TextView自动跳转应用

通过TextView.setAutoLink(int)设置其Linkify属性,TextView会自动检查其内容,并识别出电话号码、网址、邮箱地址,并标记为超链接,可以点击,点击后便跳转到相应的应用服务程序。

Linkify.EMAIL_ADDRESSES - 仅识别出TextView中的邮箱地址并标记为超链接,点击后会弹出发送邮件的对话框来提示用户是否进一步操作
Linkify.PHONE_NUMBERS   - 仅识别出TextView中的电话号码并标记为超链接,点击后会弹出拨打电话的对话框来提示用户是否进一步操作
Linkify.WEB_URLS        - 仅识别出TextView中的网址并标记为超链接,点击后会使用浏览器打开网址
Linkify.ALL             - 识别出所有系统所支持的特殊URI,然后做相应的操作

String/SpannableString/SpannableStringBuilder类图

从类图中可以清晰的看出:

  1. SpannableString实现了Spannable接口,而Spannable接口定义了可变标记的操作集,用于关联或分离标记对象,故SpannableString必须在实例化对象时传入文本内容,实例化后就不能改变其文本内容,只能设置文本的标记对象,即Span样式;

  2. SpannableStringBuilder实现Spannable接口的同时也实现了Editable接口,而Editable定义了可变标记和可变文本内容的操作集,故SpannableStringBuilder可以在实例化对象时不传入文本内容,而使用append()方法类逐步添加内容,并通过setSpan()方法设置Span样式;

  3. String、SpannableString和SpannableStringBuilder同时实现了CharSequence接口,都可以实现字符的操作。

uml_class_spannable_string.png

Span样式设置示例

  1. setSpan()方法参数flags中INCLUSIVEEXCLUSIVE的区别

    mSpanFlagsTv = (TextView) findViewById(R.id.tv_span_flags);
    mSpanFlagsEt = (EditText) findViewById(R.id.et_span_flags);
    SpannableStringBuilder spb = new SpannableStringBuilder();
    spb.append(getString(R.string.text_happy));
    spb.setSpan(new ForegroundColorSpan(Color.BLUE), 0, 2, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
    mSpanFlagsEt.setText(spb);
    mSpanFlagsTv.setText(R.string.text_span_flags);
    
  2. ForegroundColorSpan - 字体颜色 (Color.CYAN)

    String foreground = getResources().getString(R.string.text_foreground_color_span);
    SpannableString sp = new SpannableString(foreground);
    sp.setSpan(new ForegroundColorSpan(Color.RED), 0, foreground.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    mForegroundColorSpanTv.setText(sp);
    
  3. BackgroundColorSpan - 字体背景颜色 (Color.parseColor("#8FBC8F"))

    String background = getResources().getString(R.string.text_background_color_span);
    SpannableString sp = new SpannableString(background);
    sp.setSpan(new BackgroundColorSpan(Color.parseColor("#8FBC8F")), 0, background.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    mBackgroundColorSpanTv.setText(sp);
    
  4. AbsoluteSizeSpan - 字体大小 (参数是绝对数值)

    String absolute = getString(R.string.text_absolute_size_span);
    SpannableString sp = new SpannableString(absolute);
    // 设置字体大小为12,true表示单位为dp
    sp.setSpan(new AbsoluteSizeSpan(12, true), 0, absolute.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    mAbsoluteSizeSpanTv.setText(sp);
    
  5. RelativeSizeSpan - 字体大小 (参数是相对于默认字体大小的倍数)

    String relative = getString(R.string.text_relative_size_span);
    SpannableString sp = new SpannableString(relative);
    sp.setSpan(new RelativeSizeSpan(1.5f), 0, relative.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    mRelativeSizeSpanTv.setText(sp);
    
  6. TypefaceSpan - 字体 (serif/sans-serif)

    String typeface = getString(R.string.text_type_face_span);
    SpannableString sp = new SpannableString(typeface);
    // 设置字体(default,default-bold,monospace,serif,sans-serif)
    sp.setSpan(new TypefaceSpan("serif"), 0, typeface.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    mTypefaceSpanTv.setText(sp);
    
  7. StyleSpan - 字体样式 (粗体/斜体) (Typeface.BOLD_ITALIC)

    String style = getString(R.string.text_style_span); 
    SpannableString sp = new SpannableString(style);
    sp.setSpan(new StyleSpan(Typeface.ITALIC), 0, style.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    mStyleSpanTv.setText(sp);
    
  8. StrikethroughSpan - 删除线

    String strike = getString(R.string.text_strikethrough_span);
    SpannableString sp = new SpannableString(strike);
    sp.setSpan(new StrikethroughSpan(), 0, strike.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    mStrikethroughSpanTv.setText(sp);
    
  9. UnderlineSpan - 下划线

    String underline = getString(R.string.text_underline_span);
    SpannableString sp = new SpannableString(underline);
    sp.setSpan(new UnderlineSpan(), 0, underline.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    mUnderlineSpanTv.setText(sp);
    
  10. ImageSpan - 图片置换 (使用图片将文字替换)

    String image = getString(R.string.text_image_span);
    SpannableString sp = new SpannableString(image);
    // 获取Drawable资源
    Drawable drawable = ContextCompat.getDrawable(this, R.drawable.ic_face);
    drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
    sp.setSpan(new ImageSpan(drawable, ImageSpan.ALIGN_BASELINE), 0, image.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    mImageSpanTv.setText(sp);
    

TextView自动跳转设置

private void showAutoLinkTextView() {
    mUrlLinkTv = (TextView) findViewById(R.id.tv_link_url);
    mEmailLinkTv = (TextView) findViewById(R.id.tv_link_email);

    // Set the attribute first, then set the text. Otherwise, it won't work
    // or set 'android:autoLink' in layout xml
    mUrlLinkTv.setAutoLinkMask(Linkify.WEB_URLS);
    mEmailLinkTv.setAutoLinkMask(Linkify.EMAIL_ADDRESSES);

    mUrlLinkTv.setText("Home: www.baidu.com");
    mEmailLinkTv.setText("Email: shellever@163.com");
}

效果演示

spannable_string_linkify_5m.gif

Links

SpannableString与SpannableStringBuilder
android-SpannableString或SpannableStringBuilder以及string.xml文件中的整型和string型代替

源码参考

SpannableString

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

推荐阅读更多精彩内容