今天碰到了一个问题,为一段文字添加双引号,使得文字包裹在双引号内。设计稿如下:


设计稿

想了很久,感觉用控件堆不太好弄,于是询问蕾哥后知道了Spannable这个类。然后在网上查了查这个类的用法,发现用这个类就是用来实现文本的样式修改。

Spannable继承自Spanned接口,而实际上,Spanned继承自CharSequence接口。

在TextView的setText(CharSequence text)方法中,要求的参数正好是一个CharSequence对象,因此,我们可以通过Spannable对象来直接使用setText来完成文本的设置。在使用中通常使用Spannable spn = new SpannableString("字符串");或者通过SpannableStringBuilder对象来进行构建。

在构建除了Spannable对象以后,就可以使用spannable.setSpan(Obj what, int start, int end, int flags)方法来进行样式的设置了,其中参数what是具体样式的实现对象,start则是该样式开始的位置,end对应的是样式结束的位置,参数flags,定义在Spannable中的常量,常用的有:

Spanned.SPAN_EXCLUSIVE_EXCLUSIVE --- 不包含两端start和end所在的端点 (a,b)

Spanned.SPAN_EXCLUSIVE_INCLUSIVE --- 不包含端start,但包含end所在的端点 (a,b]

Spanned.SPAN_INCLUSIVE_EXCLUSIVE --- 包含两端start,但不包含end所在的端点 [a,b)

Spanned.SPAN_INCLUSIVE_INCLUSIVE --- 包含两端start和end所在的端点 [a,b]

第一个参数传入一个样式,样式种类有,具体参见这里和这里:

AbsoluteSizeSpan 指定文字大小

TypefaceSpan 可以设置不同的字体

AlignmentSpan.Standard 标准文本对齐

BackgroundColorSpan 文本背景颜色

ForegroundColorSpan 文字字体颜色

LeadingMarginSpan 文本缩进

TabStopSpan 制表位偏移样式

TextAppearanceSpan 使用style文件来定义文本样式

RelativeSizeSpan 对于文本设定的大小的相对比例

ScaleXSpan 将字体按比例进行横向缩放

URLSpan 可以打开一个链接

StyleSpan 正常、粗体、斜体和同时加粗倾斜四种样式

StrikethroughSpan 删除线样式

QuoteSpan 在文本左侧添加一条表示引用的竖线

UnderlineSpan 给一段文字加上下划线

SubscriptSpan 脚注样式,比如化学式的常见写法

SuperscriptSpan 上标样式,比如数学上的次方运算

BulletSpan 文本着重样式,类似于HTML中的

标签的圆点效果


DrawableMarginSpan 、IconMarginSpan 图片+Margin样式

ImageSpan 图片样式,主要用于在文本中插入图片 聊天中的emoji表情显示用的就是这个

MaskFilterSpan 文本滤镜 目前只有模糊效果和浮雕效果

RasterizerSpan 光栅化

通过这种方式,我实现最上面的需求,实现方法如下:

public static SpannableStringBuilder addQuoteSpannableStr(String resoneStr){
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder("“" + resoneStr + "”");
int length = spannableStringBuilder.length();
spannableStringBuilder.setSpan(new AbsoluteSizeSpan(DensityUtil.dip2px(17)), 0, 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableStringBuilder.setSpan(new AbsoluteSizeSpan(DensityUtil.dip2px(15)), 1, length - 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableStringBuilder.setSpan(new AbsoluteSizeSpan(DensityUtil.dip2px(17)), length - 1, length, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableStringBuilder.setSpan(new ForegroundColorSpan(ResourceUtil.getColor(R.color.ab_life_feed_item_recommend_quote)), 0, 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableStringBuilder.setSpan(new ForegroundColorSpan(ResourceUtil.getColor(R.color.ab_life_feed_item_recommend_resone)), 1, length - 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableStringBuilder.setSpan(new ForegroundColorSpan(ResourceUtil.getColor(R.color.ab_life_feed_item_recommend_quote)), length - 1, length, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
return spannableStringBuilder;
}

最后再附录一段添加emoji代码:

public static void convert2RichText(String text, Spannable spannable, float scaleRate) {
Pattern pattern = Pattern.compile("\\[([0-9]+)\\]");
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
String emojiId = matcher.group(1);
System.out.println("emojiId:" + emojiId);
if (EMOJI_MAP.containsKey(emojiId)) {
Drawable drawable = getEmojiDrawable(context, emojiId, scaleRate);
int imageStartIndex = matcher.start();
int imageEndIndex = matcher.end();
ImageSpan span = new ImageSpan(drawable, ImageSpan.ALIGN_BOTTOM);
spannable.setSpan(span, imageStartIndex, imageEndIndex, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
}
}
}