在说SpannableString +SpannableStringBuilder之前,感觉有必要简单提一下CharSequence:
一. 概述
1.CharSequence
CharSequence:对这个东西一开始也不了解,经上网查看.在JDK1.4中,引入了CharSequence接口,实现了这个接口的类有:CharBuffer、String、StringBuffer、StringBuilder这个四个类。
但是这个接口提供的方法有限,只有下面几个:charat、length、subSequence、toString这几个方法,感觉如果有必要,还是重载的比较好,避免用instaneof这个操作符。
2.SpannableString、SpannableStringBuilder与String的关系
首先SpannableString、SpannableStringBuilder基本上与String差不多,也是用来存储字符串,但它们俩的特殊就在于有一个SetSpan()函数,能给这些存储的String添加各种格式或者称样式(Span),将原来的String以不同的样式显示出来,比如在原来String上加下划线、加背景色、改变字体颜色、用图片把指定的文字给替换掉,等等。所以,总而言之,SpannableString、SpannableStringBuilder与String一样, 首先也是传字符串,但SpannableString、SpannableStringBuilder可以对这些字符串添加额外的样式信息,但String则不行。
注意:如果这些额外信息能被所用的方式支持,比如将SpannableString传给TextView;也有对这些额外信息不支持的,比如前一章讲到的Canvas绘制文字,对于不支持的情况,SpannableString和SpannableStringBuilder就是退化为String类型,直接显示原来的String字符串,而不会再显示这些附加的额外信息。
3、SpannableString与SpannableStringBuilder区别
它们的区别在于 SpannableString像一个String一样,构造对象的时候传入一个String,之后再无法更改String的内容,也无法拼接多个 SpannableString;而SpannableStringBuilder则更像是StringBuilder,它可以通过其append()方法来拼接多个String:
//使用SpannableString,必须一次传入,构造完成
SpannableString word = new SpannableString("欢迎光临Harvic的博客");
//使用SpannableStringBuilder,可以使用append()再添加
SpannableStringBuilder multiWord = new SpannableStringBuilder();
multiWord.append("欢迎光临");
multiWord.append("Harvic的");
multiWord.append("博客");
(转自博客:《android - SpannableString或SpannableStringBuilder以及string.xml文件中的整型和string型代替》)
因为Spannable等最终都实现了CharSequence接口,所以可以直接把SpannableString和SpannableStringBuilder通过TextView.setText()设置给TextView。
4、SetSpan()
void setSpan (Object what, int start, int end, int flags)
函数意义:给SpannableString或SpannableStringBuilder特定范围的字符串设定Span样式,可以设置多个(比如同时加上下划线和删除线等),Falg参数标识了当在所标记范围前和标记范围后紧贴着插入新字符时的动作,即是否对新插入的字符应用同样的样式。(这个后面会具体举例说明)
参数说明:
object what :对应的各种Span,后面会提到;
int start:开始应用指定Span的位置,索引从0开始
int end:结束应用指定Span的位置,特效并不包括这个位置。比如如果这里数为3(即第4个字符),第4个字符不会有任何特效。从下面的例子也可以看出来。
int flags:取值有如下四个
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE:前后都不包括,即在指定范围的前面和后面插入新字符都不会应用新样式
Spannable.SPAN_EXCLUSIVE_INCLUSIVE :前面不包括,后面包括。即仅在范围字符的后面插入新字符时会应用新样式
Spannable.SPAN_INCLUSIVE_EXCLUSIVE :前面包括,后面不包括。
Spannable.SPAN_INCLUSIVE_INCLUSIVE :前后都包括。
二、各种Span设置
在前面的一个小示例,大家应该也可以看出,要应用一个Span总共分三步:
1、构造String
2、构造Span
3、利用SetSpan()对指定范围的String应用这个Span
1、字体颜色设置(ForegroundColorSpan)
SpannableString spanString = new SpannableString("欢迎光临Harvic的博客");
//再构造一个改变字体颜色的Span
ForegroundColorSpan span = new ForegroundColorSpan(Color.BLUE);
//将这个Span应用于指定范围的字体
spanString.setSpan(span, 1, 5, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
//设置给EditText显示出来
editText.setText(spanString);效果:

2、字体背景颜色(BackgroundColorSpan)
SpannableString spanString = new SpannableString("欢迎光临Harvic的博客");
// 参数一个int类型的值,必须以0xff开始(透明度为255不透明)
BackgroundColorSpan span = new BackgroundColorSpan(0xff00xx00);
spanString.setSpan(span, 0, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
editText.setText(spanString);效果:

3、字体大小(AbsoluteSizeSpan)
SpannableString spanString = new SpannableString("欢迎光临Harvic的博客");
// 绝对大小,单位为像素(px) .
AbsoluteSizeSpan span = new AbsoluteSizeSpan(16);
// 相对大小,大小为指定的原始字体大小的几倍
// RelativeSizeSpan span = new RelativeSizeSpan(2);
spanString.setSpan(span, 2, 5, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
editText.setText(spanString);效果:

4、粗体、斜体(StyleSpan)
SpannableString spanString = new SpannableString("欢迎光临Harvic的博客");
StyleSpan span = new StyleSpan(Typeface.BOLD_ITALIC);
spanString.setSpan(span, 1, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
editText.setText(spanString);效果:

5、删除线(StrikethroughSpan)
SpannableString spanString = new SpannableString("欢迎光临Harvic的博客");
StrikethroughSpan span = new StrikethroughSpan();
spanString.setSpan(span, 2, 5, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
editText.setText(spanString);效果:

6、下划线(UnderlineSpan)
SpannableString spanString = new SpannableString("欢迎光临Harvic的博客");
UnderlineSpan span = new UnderlineSpan();
spanString.setSpan(span, 1, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
editText.setText(spanString);效果:

7、图片置换(ImageSpan)
ImagSpan有很多构造函数,一般是通过传入Drawableg来构造
SpannableString spanString = new SpannableString("欢迎光临Harvic的博客");
Drawable d = getResources().getDrawable(R.drawable.ic_launcher);
d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
ImageSpan span = new ImageSpan(d, ImageSpan.ALIGN_BASELINE);
spanString.setSpan(span, 2, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
editText.setText(spanString);效果:

这个函数的不同之处在于,前几都是在原来文字的基础上加上特效,而这里却是利用图片将文字替换。如果遇到不支持显示图片的函数,比如前一篇中的canvas绘图。就会退化成String,即以原来的String字符串来显示。
另附案例:简单表情包获取(很不完善啊….)
// OnCreate函数
private TextView view;
private Map<String, Drawable> map = new HashMap<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
view = (TextView) findViewById(.main_text);
SpannableString spannableString = new SpannableString("0123456789");
initEmoJi();
String str = "[呵呵]sdhjfgkh[尴尬]sdf[滚粗]nighjk";
SpannableString spannableString1 = buildSpannableString(str);
view.setText(spannableString1);
}
// 初始化这个表情
public void initEmoJi() {
Drawable d01 = getResources().getDrawable(R.mipmap.ic_launcher);
d01.setBounds(0,0,50,50);
map.put("尴尬",d01);
Drawable d02 = getResources().getDrawable(R.mipmap.ic_launcher);
d02.setBounds(0,0,50,50);
map.put("发火",d02);
Drawable d03 = getResources().getDrawable(R.mipmap.ic_launcher);
d03.setBounds(0,0,50,50);
map.put("呵呵",d03);
Drawable d04 = getResources().getDrawable(R.mipmap.ic_launcher);
d04.setBounds(0,0,50,50);
map.put("滚粗",d04);
}
public SpannableString buildSpannableString(String src) {
SpannableString spannableString = new SpannableString(src);
// 勉强模式
Pattern pattern = Pattern.compile("\\[(.+?)\\]");
Matcher matcher = pattern.matcher(src);
while (matcher.find()){
String group = matcher.group(1);
Drawable drawable = map.get(group);
ImageSpan imageSpan = new ImageSpan(drawable);
if (imageSpan != null) {
spannableString.setSpan(imageSpan,matcher.start(),matcher.end(),Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
}
return spannableString;
}
















