场景
在实际开发中,经常会出现一些富文本的样式(如:局部文本可点击,超链接,一段文本字体大小不一,局部文本加粗等),这样常规的设置文本就无法实现目的了。
针对这种场景一些常规的实现:
- 利用多个View的排布来实现;
- 利用Html实现富文本格式,在通过Html.getText(html)来解析;
- 利用SpannableString;
方案分析:
- 使用方案一会在布局中新增出多个View,导致布局文件臃肿,并且View的inflate是一件比较耗时的操作。当然在一些特殊的场景,为了简单快速的实现目的,这种方案也有应用。另外就是采用此方案有时并不能完美的实现我们想要的功能;
- 使用方案二,优点是可以很好的实现我们想要的文本样式,但缺点也很明显,Html.getText()方法解析html标签是一个很消耗性能的操作,一般情非得已不推荐使用此方案;
- 方案三就是本篇要介绍的对象,SpannableString是Android提供专门用于处理富文本的类,根据设置不同的span来实现不同的文本样式。使用一个TextView通过span的处理,即可实现目的。
API
SpannaableString与SpannableStringBuilder的区别就在于builder可以使用insert、append等系列方法。
-
public void setSpan():设置span,一切富文本的实现都是通过此方法的来设置不同效果的span来实现的。
/** * * @param what 具体的实现效果的Span类对象 * @param start span生效的起始下标 * @param end span生效的终止下标+1 * @param flags span效果针对临界位置的insert是否扩散生效 */ public void setSpan(Object what, int start, int end, int flags) { }
public void removeSpan(Object what):移除指定的span;
-
insert/delete/append等方法是SpannableStringBuilder所有的方法系列,其中setSpan()中的flags参数就是配合这些方法的使用而发挥效果的。
flags的选项在Spanned接口中,分别为:
- SPAN_INCLUSIVE_EXCLUSIVE:包含start,不包含end
- SPAN_INCLUSIVE_INCLUSIVE:start,end都包含
- SPAN_EXCLUSIVE_EXCLUSIVE:start,end都不包含
- SPAN_EXCLUSIVE_INCLUSIVE:start不包含,end包含
具体生效备注如下:
Spanned中flags的标记,是在SpannableStringBuilder中使用的,在SpannableString中没有作用。当使用了SpannableStringBuilder.insert(int,CharSeque)系列方法后,对于insert后的字符串是否进行扩展特性的标记,并且此标记作用的场景也仅仅是insert的位置恰好处于start或者end两个端点的临界位置,即用flags标记这个临界点跟随哪个,是否使用span的特性。
各种Span
-
ForegroundColorSpan
用于设置前景色,即设置字体的颜色
void forefroundColorSpan() { TextView tv1 = new TextView(activity); /** * Spanned中flags的标记,是在SpannableStringBuilder中使用的,在SpannableString中没有作用。并且其使用场景是当 使用了SpannableStringBuilder.insert(int,CharSeque)方法后,,对于insert后的字符串是否进行扩展特性的标记,, 并且此标记作用的场景也仅仅是insert的位置恰好处于start 或者 end两个端点的临界位置,,,即用flags标记这个临界点跟随哪个。 * */ SpannableStringBuilder ss = new SpannableStringBuilder("手续费84.00元"); ForegroundColorSpan span1 = new ForegroundColorSpan(Color.RED); ss.setSpan(span1, 3, 8, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); tv1.setText(ss); rootSpannable.addView(tv1); //插入的9扩展了特性,而插入的5未扩展特性 ss.insert(3, "9").insert(9, "5"); TextView tv2 = new TextView(activity); tv2.setText(ss); rootSpannable.addView(tv2); //移除指定span ss.removeSpan(span1); TextView tv5 = new TextView(activity); tv5.setText(ss); rootSpannable.addView(tv5); //恢复ss ss = new SpannableStringBuilder("手续费84.00元"); ss.setSpan(span1, 3, 8, Spanned.SPAN_EXCLUSIVE_INCLUSIVE); //验证同样的情况,只要执行insert系列方法,,即使flags改变效果也不变 TextView tv3 = new TextView(activity); tv3.setText(ss); rootSpannable.addView(tv3); //插入的9未扩展特性,而插入的5扩展了特性 ss.insert(3, "9").insert(9, "5"); TextView tv4 = new TextView(activity); tv4.setText(ss); rootSpannable.addView(tv4); }
效果图如下:
-
ClickableSpan
设置局部文本可点击
void clickableSpan() { ClickableSpan clickableSpan = new ClickableSpan() { @Override public void updateDrawState(TextPaint ds) { ds.setColor(Color.BLUE);//控制可点击文案的字体颜色 ds.setUnderlineText(false);//可点击文案是否具有下划线 } @Override public void onClick(View widget) { MyToast.showShort(getContext(), "clicked"); } }; SpannableString ss = getSS(); ss.setSpan(clickableSpan, 0, 3, Spanned.SPAN_EXCLUSIVE_INCLUSIVE); TextView tv = getTv(); tv.setText(ss); tv.setMovementMethod(LinkMovementMethod.getInstance()); //一般由于页面的数据刷险等原因,为避免重复设置MovementMethod,在setMovementMethod之前做如下下判断 /*MovementMethod m = tv.getMovementMethod(); if (m == null || !(m instanceof LinkMovementMethod)) { if (tv.getLinksClickable()) { tv.setMovementMethod(LinkMovementMethod.getInstance()); } }*/ tv.setHighlightColor(Color.YELLOW);//更改可点击区域文本,触摸或者点击后的背景高亮的显示颜色 }
效果如下:
-
BackgroundColorSpan
背景色,设置局部的TextView具有背景
-
MaskFilterSpan
修饰效果,如模糊(BlurMaskFilter)浮雕(EmbossMaskFilter)
-
RasterizerSpan
光栅效果
-
StrikethroughSpan
删除线(中间线)
-
SuggestionSpan
相当于占位符
-
UnderlineSpan
下划线
-
AbsoluteSizeSpan
绝对大小(文本字体)
// 设置字体绝对大小(绝对值,单位:像素) msp.setSpan(new AbsoluteSizeSpan(20), 4, 6, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); // 第二个参数boolean dip,如果为true,表示前面的字体大小单位为dip,否则为像素,同上。 msp.setSpan(new AbsoluteSizeSpan(20, true), 6, 8,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-
DynamicDrawableSpan
设置图片,基于文本基线或者底部对齐
-
ImageSpan
图片
-
RelativeSizeSpan
相对大小(文本字体)
设置字体相对大小(相对值,单位:像素)参数表示为默认字体大小的多少倍
msp.setSpan(new RelativeSizeSpan(0.5f), 8, 10, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); // 0.5f表示默认字体大小的一半
-
ReplacementSpan
父类,一般不用
-
MetricAffectingSpan
父类,一般不用
-
ScaleSpan
基于X轴缩放
-
StyleSpan
new StyleSpan(Typeface.BOLD)
字体样式:粗体、斜体等
-
SubscriptSpan
下标(数学公式会用到)
-
SuperscriptSpan
上标(数学公式会用到)
-
TextAppearanceSpan
文本外貌(包括字体、大小、样式、颜色)
-
TypefaceSpan
文本字体
-
URLSpan
文本超链接