前言

前段时间看过一篇 实现类似新浪微博帖子显示(2)——话题、@好友、表情解析工具类类似这种富文本的显示我们一般首先就会想到SpannableString,原作者也是这样实现的,就想着整理一下 相关知识.

SpannableString与SpannableStringBuilder

从名称来看一个是 builder, 一个是 string,很好理解.关于 SpannableString,SpannableStringBuilder及String的区别可以看这里

SpannableString与SpannableStringBuilder,SpannableStringBuilder可以通过append()方法来动态改变其内部的值,与String不同的是前两者都可以通过span来添加额外的信息.

SetSpan方法

方法声明如下:

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

参数说明:

what : 文本格式,可以设置成前景色,背景色,下划线,中划线,模糊等

start : 字符串设置格式的起始下标

end : 字符串设置格式结束下标

flags : 标识

flags: 包含四种情况,用四个常量控制

Spanned.SPAN_INCLUSIVE_EXCLUSIVE 从起始下标到结束下标,包括起始下标不包含结束坐标

Spanned.SPAN_EXCLUSIVE_EXCLUSIVE 从起始下标到结束下标,但都不包括起始下标和结束下标

Spanned.SPAN_INCLUSIVE_INCLUSIVE 从起始下标到终了下标,同时包括起始下标和结束下标

Spanned.SPAN_EXCLUSIVE_INCLUSIVE 从起始下标到终了下标,包括结束下标不包含起始坐标

Spans框架

关于Android 平台上的Spans架构,可以查看Spans, a Powerful Concept., Spans主要分为四个层次

如果一个Span影响字符级的文本格式,则继承CharacterStyle.包括我们常用的ForegroundColorSpan,BackgroundColorSpan.

如果一个Span影响段落层次的文本格式,则实现ParagraphStyle.包括了BulletSpan,继承自LeadingMarginSpan

如果一个Span修改字符级别的文本外观,则实现UpdateAppearance.包括了ClickableSpan

如果一个Span修改字符级文本度量|大小,则实现UpdateLayout.包括了AbsoluteSizeSpan

具体其继承结构可通过查看其Hierarchy

扩展阅读:

工作原理

当你给一个TextView设置文本时,它使用Layout去管理文本的渲染。Layout包含有三个子类.分别是

BoringLayout:负责显示单行文本,isBoring用于判断是否是单行文本.

DynamicLayout:负责渲染Spannable,且内部会设置SpanWatcher,有soan的时候会reflow,进而重新计算布局.

StaticLayout: 单行文本,且非Spannable的时候,不会监听span变化,效率较DynamicLayout高.里面处理了换行.

Layout.draw()会负责文本的绘制,其中drawBackground会递归LineBackgroundSpan并调用lineBackgroundSpan.drawBackground来进行背景的绘制.

drawText则会首先通过TextLine.obtain()生成TextLine,如果是文本,则调用canvas.drawText绘制,如果包含了Spannble,emoji,则交给TextLine绘制.

TextLine#draw()会调用drawRun,进而调用handleRun()进行文本的渲染.

TextLayoutCache为了提高效率,在4.0之后加入.

如果想要提升TextView的渲染效率,可以使用StaticLayout.

扩展阅读:

自定义Span

除了上述四类 Span,我们还可以自定义Span,自定义Span如同自定义View一样.可以继承已有的Span(扩展),也可以通过继承抽象了或者接口完全自定义.

扩展已有的Span

类似demo中扩展ForegroundColorSpan来实现的ActionBar的淡入效果.这里需要借助属性动画的相关知识.

一般需要复写updateDrawState,getSize或者draw方法.

public void updateDrawState(TextPaint ds){}

updateDrawState方法最终会被TextLine#handleRun()方法调用

public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {}

getSize()方法,返回新的更换Span后的size

@Override

public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {

//draw something

canvas.drawText(text, start, end, x, y, paint);

}

draw使用Canvas绘制一些文本之外的东西.可以是 背景..

完全自定义

通过源码我们发现CharacterStyle,ParagraphStyle.UpdateAppearance和UpdateLayout都是一个空接口.我们要实现自定义Span,则只有继承自其子类,一般是MetricAffectingSpan,ReplacementSpan,LineBackgroundSpan

比如,如果需要自定义背景则可以继承LineBackgroundSpan,就像Demo中的LetterLineBackgroundSpan

学习demo:flavienlaurent/spans,里面列举了各种Span的用法.

FontMetrics

就是所谓的字体规格,如下图所示

FontMetrics是Paint的内部类,里面包含了一些关于字体的常量.

其中Baseline(基线),ascent(上坡度),descent(下坡度),leading(行间距),这些常量集合canvas使得我们的绘制工作变得更加的自由

而我们绘制文字的时候一般使用的是TextPaint这个继承自Paint的类.

//获取文本宽度

TextPaint textPaint = new TextPaint();

paint.setTextSize(size);//设置字体大小

paint.setTypeface(Typeface.xx);//设置字体

float width = Layout.getDesiredWidth(str,textPaint);

图文混排居中

比如,我们在使用ImageSpan的时候,并且使用了lineSpacingExtra来设置行间距后,会出现图片下沉,即图片和文字不再一条线上,这时候就可以通过FontMetrics来设置改变.如,评论中插入 图像,将span上移即可

解决方法

public class EmojiSpan extends ImageSpan {

public EmojiSpan(Bitmap drawable) {

super(drawable);

}

@Override

public void draw(@NonNull Canvas canvas, CharSequence text,

int start, int end, float x,

int top, int y, int bottom, @NonNull Paint paint) {

// image to draw

Drawable b = getDrawable();

// font metrics of text to be replaced

Paint.FontMetricsInt fm = paint.getFontMetricsInt();

int transY = (y + fm.descent + y + fm.ascent) / 2

- b.getBounds().bottom / 2;

canvas.save();

canvas.translate(x, transY);

b.draw(canvas);

canvas.restore();

}

}

扩展阅读

android string 原理,Android中的SpannableString,Spans以及TextView绘制原理相关推荐

  1. Android面试收集录12 View测量、布局及绘制原理

    一.View绘制的流程框架 View的绘制是从上往下一层层迭代下来的.DecorView-->ViewGroup(--->ViewGroup)-->View ,按照这个流程从上往下, ...

  2. 从Android 6.0源码的角度剖析View的绘制原理

    在从Android 6.0源码的角度剖析Activity的启动过程和从Android 6.0源码的角度剖析Window内部机制原理的文章中,我们分别详细地阐述了一个界面(Activity)从启动到显示 ...

  3. 减号android string,关于android:java.lang.numberformatexception:无效的双精度字符:“”...

    收到无效Double的错误 java.lang.numberformatexception无效的double: 这是什么原因 活动1 package com.example.solarcalculat ...

  4. android string 过滤,Android 字符串过滤器InputFilter详解

    概述 InputFilter是系统提供的一个接口,里面只有一个方法filter(),用于过滤输入/插入的字符串,返回值为CharSequence. 一般都是通过判断语句来过滤字符串,在这里 Input ...

  5. 字典哈希表的实现原理_GCC中unordered_(multi)set/map的实现原理 (Part 2 图解哈希表结构)...

    写在前面 (本专栏仅是个人笔记本,有胡言乱语和错漏部分) 本文以图文+代码的形式记录了_Hashtable的结构,如何编排每一个bucket的链表,如何将每个bucket的链表串在一起形成一个长链表, ...

  6. php自动加载原理,php中的自动加载类机制原理

    PHP最早讀取套件的方法 初學PHP時,最早會面對的問題之一就是require與include差別何在? require_once與include_once又是什麼? 弄懂這些問題之後,如果不使用fr ...

  7. android开发actionbar,Android开发之自定义ActionBar和TitleBar

    首先给出代码和效果 private void configureActionBar() { LayoutInflater inflater = (LayoutInflater) getSystemSe ...

  8. android中多态的应用_动态代理原理及在 Android 中的应用

    code小生 一个专注大前端领域的技术平台公众号回复Android加入安卓技术群 作者:trampcr 链接:https://www.jianshu.com/p/492903ab2fae 声明:本文已 ...

  9. android广播intent原理,Android中BroadcastReceiver详解

    BroadcastReceiver是什么? Android app可以发送广播也可以接收系统或者其它app发送的广播,是发送/订阅的设计模式.这些广播被发送当重要的事件发生的时候.例如,安卓系统发送广 ...

最新文章

  1. MAXIEYE创始人周圣砚:以规模化迎接智能驾驶科技平权时代 | MEET2022
  2. merge into语句的使用
  3. tp框架中的一些疑点知识-5
  4. Confluence 6 启用远程 API
  5. linux设备驱动学习(三)——并发控制
  6. 计算虚拟化涉及的关键技术有哪几项_都开始商用了 5G的这些关键技术还不知道?...
  7. android局部翻转动画,android 围绕中心旋转动画
  8. angular 居中_Angular Material design设计
  9. Vue提示warn:”[vue-router] Named Route ‘home’ has a default child route…”
  10. Segment Routing MPLS介绍
  11. Selenium2用最简xpath查找元素
  12. Python中requests上传大文件
  13. 百度网盘百度云倍速播放破解
  14. 云计算概念及发展历程
  15. 联发科mt8516价格_一颗神U创造历史:联发科MT8516
  16. R语言通过WinBUGS对MGARCH和MSV模型进行贝叶斯估计和比较
  17. 用什么软件可以记录并提醒每天的工作任务?
  18. speedoffice(word)如何给文字加粗
  19. PHP之两个日期之间相差天数
  20. 2021年最新阿里面经分享,复盘我的阿里巴巴三轮技术面,希望对大家有帮助!

热门文章

  1. 【企业数字化转型】数字化转型的本质(学习笔记)
  2. MySQL 报错:ERROR 2002 (HY000): Can't connect to local MySQL server through socket
  3. 天津大学《计算机应用基础》在线
  4. python自定义安装选项_Python安装教程详解
  5. Java分离中文姓名姓氏和名字
  6. 大唐杯比赛辅导,国一选手
  7. 智慧工厂之化工厂人员定位系统,工厂实时定位,视频联动-新导智能
  8. 中南林业科技大学Java实验报告十二:数据库系统设计 - 从0到1搭建java可视化学生管理系统源代码
  9. 【MySQL数据库】笔试题总结
  10. 九龙证券|近50亿资金抢筹券商龙头,知名游资杀入热门互联网股