Span 介绍及使用(一)

之前有个项目需要对文字实现局部点击;实现时用到了Span,感觉很有意思,所以记录一下。

Span主要用于TextView中内容的显示,可以控制内容的大小,样式,颜色,文字风格,文字的字形等。功能十分的强大。

Span只是Android开发中的一个概念,并不是一个类,或者是一个接口;因为TextView可以显示文字,图片;显示文字时也有多有少,有时可能要控制某一个段落的文字的样式;也有时要控制某几个字符的文本格式或者是修改文本的外观;甚至有时还要对文本的大小等进行修改;所以Span也要分上几大类:

  1. 修改字符文本格式时 使用 CharacterStyle
  2. 修改字符外观时 使用 UpdateAppearance
  3. 修改文字段落格式时 使用 ParagraphStyle
  4. 修改文字大小度量时 使用 UpdateLayout

下面是从AndroidStudio截取的上面这四个类或者接口的子类图:

CharacterStyle 的子类图:
CharacterStyle 子类图
根据字面意思,我们可以看到,里面有 控制背景色的Span,控制点击的Span,控制前景色的Span,给文字添加马赛克过滤功能的Span,给文字添加删除线的Span,给文字添加下划线的Span等。

UpdateAppearance 的子类图:
UpdateAppearance 的子类图

根据字面意思,我们可以看到,里面有的Span跟CharacterStyle里的功能很多一样;但是它有一个子接口 Updatelayout

ParagraphStyle 的子接口图:


ParagraphStyle 子接口图

这个里面有 控制文字对齐的Span,控制文字头部样式的Span等。

UpdateLayout 的子类图:
UpdateLayout子类图
这个里面有控制文字绝对/相对大小的Span,文字风格的Span,X轴文字缩放的Span等。

上面可能看的比较乱,下面附上几张在flavienlaurent 上获取的 beautiful class diagrams :
character_style
paragraph_style
update_appearance
update_layout

TextView调用setText()时,它使用布局的基类Layout来进行文字的渲染。Layout类里会在渲染的时候去判断当前的文本是否是一个Spanned(Spanned是Spannable的父接口,SpannableString是Spannable的子类),如果是Spanned就会使用Spanned去负责文本的显示。下面是LayoutdrawText()的一部分代码:

public void drawText(Canvas canvas, int firstLine, int lastLine) {
    // ……
    // 省略一大部分代码
    //……
    ParagraphStyle[] spans = NO_PARA_SPANS;
    TextLine tl = TextLine.obtain();
		//判断是否是一个Spanned
        if (mSpannedText) {
            Spanned sp = (Spanned) buf;
            int textLength = buf.length();
            boolean isFirstParaLine = (start == 0 || buf.charAt(start - 1) == '\n');
            if (start >= spanEnd && (lineNum == firstLine || isFirstParaLine)) {
                spanEnd = sp.nextSpanTransition(start, textLength,
                        ParagraphStyle.class);
				//这里只会处理ParagrahSpan 
                spans = getParagraphSpans(sp, start, spanEnd, ParagraphStyle.class);
                paraAlign = mAlignment;
                for (int n = spans.length - 1; n >= 0; n--) {
                    if (spans[n] instanceof AlignmentSpan) {
                        paraAlign = ((AlignmentSpan) spans[n]).getAlignment();
                        break;
                    }
                }
                tabStopsIsInitialized = false;
            }
        }
    // ……
    // 省略一大部分代码
    //……
}

上面代码有个变量 mSpannedText,它是在构造方法里这样赋值的 mSpannedText = text instanceof Spanned;;这样判断当前文本是否是一个Spanned.

Layout调用draw()时会先后调用drawBackground()drawText();一个进行背景的绘制,一个进行文字的绘制。在Layout里,还有一个TextLine 类,这个才是实际渲染文字类,它会对文字逐行渲染。

TextLine tl = TextLine.obtain();

	// Draw the lines, one at a time.
	// The baseline is the top of the following line minus the current line's descent.
	for (int i = firstLine; i <= lastLine; i++) {
	     // ……
   		 // 省略一部分代码
    	 //……
	      Directions directions = getLineDirections(i);
	      if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTabOrEmoji) {
	         // XXX: assumes there's nothing additional to be done
	          canvas.drawText(buf, start, end, x, lbaseline, paint);
	      } else {
	          tl.set(paint, buf, start, end, dir, directions, hasTabOrEmoji, tabStops);
	          tl.draw(canvas, x, ltop, lbaseline, lbottom);
	      }
	}
TextLine.recycle(tl);

其实在TextLine类里,包含了三个关于Span的集合。

private final SpanSet<MetricAffectingSpan> mMetricAffectingSpanSpanSet =
        new SpanSet<MetricAffectingSpan>(MetricAffectingSpan.class);
private final SpanSet<CharacterStyle> mCharacterStyleSpanSet =
        new SpanSet<CharacterStyle>(CharacterStyle.class);
private final SpanSet<ReplacementSpan> mReplacementSpanSpanSet =
        new SpanSet<ReplacementSpan>(ReplacementSpan.class);

总结:

关于Span的内容很复杂,上面说的有些乱,关于各个Span的具体应用,到下一节介绍。

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值