Android深入理解文字绘制:FontMetrics字体测量及其TextPaint介绍
文章目录
- TextPaint介绍
- 一、FontMetrics
- 1.1 理论知识
- 1.2 代码验证
- 1.3 fontMetrics中的变量和文字的size、typeface有关
- 1.4 绘制居中屏幕的文字
- 二、TextPaint中的各种方法
- 三、Typeface中的方法
- 3.2 扩展到TextView
TextPaint介绍
TextPaint
是Paint
的子类,用它可以很方便的进行文字的绘制,一般情况下遇到绘制文字的需求时,我们一般用TextPaint
所提供的方法。开始学习如何绘制文字之前,我们必须要先了解下android
中文字是怎么绘制到屏幕上的,文字的格式又是怎么样的。
一、FontMetrics
1.1 理论知识
它是一个Paint
的内部类,作用是“字体测量”。它里面呢就定义了top,ascent,descent,bottom,leading
五个成员变量其他什么也没有,和rect
很相似。如果你不信,我们可以去看看源码:
/*** Class that describes the various metrics for a font at a given text size.* Remember, Y values increase going down, so those values will be positive,* and values that measure distances going up will be negative. This class* is returned by getFontMetrics().*/public static class FontMetrics {/*** The maximum distance above the baseline for the tallest glyph in* the font at a given text size.*/public float top;/*** The recommended distance above the baseline for singled spaced text.*/public float ascent;/*** The recommended distance below the baseline for singled spaced text.*/public float descent;/*** The maximum distance below the baseline for the lowest glyph in* the font at a given text size.*/public float bottom;/*** The recommended additional space to add between lines of text.*/public float leading;}
为了很好的理解这5个变量的意义,我们用下面的图示来进行说明。
Baseline
是基线,在Android
中,文字的绘制都是从Baseline
处开始的ascent
意为上坡度:Baseline
往上至字符“最高处”的距离我们称之为ascent
,descent
意为下坡度:Baseline
往下至字符“最低处”的距离我们称之为descent
;leading(行间距)
则表示上一行字符的descent
到该行字符的ascent
之间的距离;top
和bottom
文档描述地很模糊,其实这里我们可以借鉴一下TextView
对文本的绘制,TextView
在绘制文本的时候总会在文本的最外层留出一些内边距,为什么要这样做?因为TextView
在绘制文本的时候考虑到了类似读音符号,下图中的A上面的符号就是一个拉丁文的类似读音符号的东西:
top
的意思其实就是除了Baseline
到字符顶端的距离外还应该包含这些符号的高度,bottom
的意思也是一样。一般情况下我们极少使用到类似的符号所以往往会忽略掉这些符号的存在,但是Android
依然会在绘制文本的时候在文本外层留出一定的边距,这就是为什么top
和bottom
总会比ascent
和descent
大一点的原因。而在TextView
中我们可以通过xml
设置其属性android:includeFontPadding="false"
去掉一定的边距值但是不能完全去掉。
1.2 代码验证
为了测试一下上述的理论是否正确,我们写下了下面的代码:
private static final String TEXT = "ap卡了ξτβбпшㄎㄊěǔぬも┰┠№@↓"; @Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);mTextPaint.setTextSize(50); mTextPaint.setColor(Color.BLACK); FontMetrics fontMetrics = mTextPaint.getFontMetrics(); Log.d("Aige", "ascent:" + fontMetrics.ascent); Log.d("Aige", "top:" + fontMetrics.top); Log.d("Aige", "leading:" + fontMetrics.leading); Log.d("Aige", "descent:" + fontMetrics.descent); Log.d("Aige", "bottom:" + fontMetrics.bottom); mTextPaint.clearShadowLayer();canvas.drawText(TEXT, 0, Math.abs(fontMetrics.top), mTextPaint);}
结果:
打印的Log:ascent:-46.38672
top:-52.807617
leading:0.0
descent:12.207031
bottom:13.549805
注:Baseline上方的值为负,下方的值为正
我们来分析一下这个结果:
因为基线上方为负,所以ascent
和top
的值都是负数,而且top
要大于ascent
,原因是要为符号留出位置。
因为只有一行文本所以leading
恒为0。
基线下方为正,所以descent
和bottom
都是正的,bottom
要略大于descent
在得到的结果中,我们发现文字是紧紧贴着屏幕顶端的,再看下我们的程序代码:
canvas.drawText(TEXT, 0, Math.abs(fontMetrics.top), mTextPaint);
x坐标
是0,y坐标
是Math.abs(fontMetrics.top)
,因为android
是从基线开始绘制的,所以我们为了让字体顶端紧贴屏幕就必须让它移下来一点,移动的距离是top
的距离,也就是基线到文字对顶部的距离。有人可能会问,如果不设置呢?x,y坐标都是0,是什么效果呢?因为android
会从基线开始绘制,所以如果不做处理,基线就是屏幕的顶部,因此会出现如下的效果:
最终,我们验证了上面的理论是完全正确的。
1.3 fontMetrics中的变量和文字的size、typeface有关
从代码中我们可以看到一个很特别的现象,在我们绘制文本之前我们便可以获取文本的FontMetrics
属性值,也就是说我们FontMetrics
的这些值跟我们要绘制什么文本是无关的,而仅与绘制文本Paint
的size
和typeface
有关。当你改变了paint
绘制文字的size
或typeface
时,FontMetrics
中的top
、bottom
等值就会发生改变。如果我们仅仅更改了文字,这些值是不会发生任何改变的。
1.4 绘制居中屏幕的文字
我们知道了这些理论知识,也知道android
是怎么绘制文字的,一会我们要做一个实际的例子来巩固巩固。首先,我们要先来扩展认识两个方法:
float android.graphics.Paint.descent()
解释:the distance below (positive) the baseline (descent) based on the current typeface and text size.
一句话解释:得到下坡度的值float android.graphics.Paint.ascent()
解释:the distance above (negative) the baseline (ascent) based on the current typeface and text size. 一句话解释:就是得到上坡度的值
实际代码:
@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);mTextPaint.setTextSize(50);mTextPaint.setColor(Color.BLACK);// 计算Baseline绘制的起点X轴坐标 ,计算方式:画布宽度的一半 - 文字宽度的一半int baseX = (int) (canvas.getWidth() / 2 - mTextPaint.measureText(TEXT) / 2);// 计算Baseline绘制的Y坐标 ,计算方式:画布高度的一半 - 文字总高度的一半int baseY = (int) ((canvas.getHeight() / 2) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2));// 居中画一个文字canvas.drawText(TEXT, baseX, baseY, mTextPaint);mPaint.setColor(Color.RED);mPaint.setStrokeWidth(2);// 为了便于理解我们在画布中心处绘制一条中线canvas.drawLine(0, canvas.getHeight() / 2, canvas.getWidth(), canvas.getHeight() / 2, mPaint);}
我们计算了x坐标
和y坐标
。
x坐标
的计算方法是(屏幕宽度-文字宽度)/2
,如果文字宽度比屏幕宽度长得到的就是负数,如果文字宽度比屏幕宽度短,得到的就是正数,这个很容易理解;y坐标
的的计算方式是(屏幕高度-文字高度)/2
,这里的文字高度用的是:descent+ascent(忽略了音标)
。
结果:
二、TextPaint中的各种方法
- float ascent()
顾名思义就是返回上坡度的值 - float descent()
得到下坡度的值 - breakText()
public int breakText (String text, boolean measureForwards, float maxWidth, float[] measuredWidth)
public int breakText (char[] text, int index, int count, float maxWidth, float[] measuredWidth)
public int breakText (CharSequence text, int start, int end, boolean measureForwards, float maxWidth, float[] measuredWidth)
这个方法让我们设置一个最大宽度,在不超过这个宽度的范围内返回实际测量值否则停止测量。
text
: 表示我们的字符串;
start
: 表示从第几个字符串开始测量;
end
: 表示从测量到第几个字符串为止;
measureForwards
: 表示向前还是向后测量;
maxWidth
: 表示一个给定的最大宽度在这个宽度内能测量出几个字符;
measuredWidth
: 为一个可选项,可以为空,不为空时返回真实的测量值
这些方法在一些结合文本处理的应用里比较常用,比如文本阅读器的翻页效果,我们需要在翻页的时候动态折断或生成一行字符串,这就派上用场了~
- getFontMetrics()
得到一个FontMetrics
对象。 - getFontMetrics (Paint.FontMetrics metrics)
这个和我们之前用到的getFontMetrics()
相比多了个参数,getFontMetrics()
返回的是FontMetrics
对象,而getFontMetrics(Paint.FontMetrics metrics)
返回的是文本的行间距,如果metrics
的值不为空则返回FontMetrics
对象的值。 - getFontMetricsInt()
该方法返回了一个FontMetricsInt
对象,FontMetricsInt
和FontMetrics
是一样的,只不过getFontMetricsInt()
得到的对象中的参数都是int
类型,而getFontMetrics()
返回对象中的参数都是float
。 - getFontMetricsInt(Paint.FontMetricsInt fmi)
得到文字的间距,距离是int
类型 - getFontSpacing()
返回字符行间距 - setUnderlineText(boolean underlineText)
设置文字的下划线 - setTypeface(Typeface typeface)
设置字体类型,上面我们也使用过。Android
中字体有四种样式:BOLD(加粗)
,BOLD_ITALIC(加粗并倾斜)
,ITALIC(倾斜)
,NORMAL(正常)
;
Android
为我们提供的字体有五种:DEFAULT, DEFAULT_BOLD, MONOSPACE, SANS_SERIF 和 SERIF
,我们也可以用自己定义的字体:
Paint p = new Paint();
String familyName = "宋体";
Typeface font = Typeface.create(familyName, Typeface.BOLD);
p.setColor(Color.RED);
p.setTypeface(font);
- setTextSkewX(float skewX)
设置文本在水平方向上的倾斜。这个倾斜值没有具体的范围,但是官方推崇的值为-0.25可以得到比较好的倾斜文本效果,值为负右倾值为正左倾,默认值为0。
- setTextSize (float textSize)
设置文字的大小,但是要注意该值必需大于零。 - setTextScaleX (float scaleX)
将文本沿X轴水平缩放,默认值为1,当值大于1会沿X轴水平放大文本,当值小于1会沿X轴水平缩放文本
// 设置画笔文本倾斜
textPaint.setTextScaleX(0.5F);
// 设置画笔文本倾斜
textPaint.setTextScaleX(1.5F);
注意:setTextScaleX
不仅放大了文本宽度同时还拉伸了字符!这是亮点~
- setTextLocale (Locale locale)
设置地理位置,这里如果你要使用,直接传入Locale.getDefault()
即可。 - setTextAlign (Paint.Align align)
设置文本的对齐方式,可供选的方式有三种:CENTER, LEFT 和 RIGHT
。
我们的文本大小是通过size
和typeface
确定的(其实还有其他的因素但这里影响不大忽略),一旦baseline
确定,对不对齐好像不相干吧。但是,你要知道一点,文本的绘制是从baseline
开始没错,但是是从哪边开始绘制的呢?左端还是右端呢?而这个Align
就是为我们定义在baseline
绘制文本究竟该从何处开始,上面我们在进行对文本的水平居中时是用Canvas
宽度的一半减去文本宽度的一半:
@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);mTextPaint.setTextSize(50);mTextPaint.setColor(Color.BLACK);// 计算Baseline绘制的起点X轴坐标 ,计算方式:画布宽度的一半 - 文字宽度的一半int baseX = (int) (canvas.getWidth() / 2 - mTextPaint.measureText(TEXT) / 2);// 计算Baseline绘制的Y坐标 ,计算方式:画布高度的一半 - 文字总高度的一半int baseY = (int) ((canvas.getHeight() / 2) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2));// 居中画一个文字canvas.drawText(TEXT, baseX, baseY, mTextPaint);mPaint.setColor(Color.RED);mPaint.setStrokeWidth(2);// 为了便于理解我们在画布中心处绘制一条中线canvas.drawLine(0, canvas.getHeight() / 2, canvas.getWidth(), canvas.getHeight() / 2, mPaint);}
实际上我们大可不必这样计算,我们只需设置Paint
的文本对齐方式为CENTER
,drawText
的时候起点x = canvas.getWidth() / 2
即可。产生的效果是,文字先算好一个基准线,从这个基准线的中点开始向左右开始绘制文字,最终自然就变成了居中显示了。如果你设定了RIGHT
,那么从baseline
的右边的顶点开始,文字开始慢慢绘制。
textPaint.setTextAlign(Align.CENTER);
canvas.drawText(TEXT, canvas.getWidth() / 2, baseY, textPaint);
当我们将文本对齐方式设置为CENTER
后就相当于告诉Android
我们这个文本绘制的时候从文本的中点开始向两端绘制;如果设置为LEFT则从文本的左端开始往右绘制;如果为RIGHT
则从文本的右端开始往左绘制:
- setSubpixelText (boolean subpixelText)
设置是否打开文本的亚像素显示,什么叫亚像素显示呢?你可以理解为对文本显示的一种优化技术,如果大家用的是Win7+
系统可以在控制面板中找到一个叫ClearType
的设置,该设置可以让你的文本更好地显示在屏幕上就是基于亚像素显示技术。 - setStrikeThruText (boolean strikeThruText)
文本删除线 - setLinearText (boolean linearText)
设置是否打开线性文本标识,这玩意对大多数人来说都很奇怪不知道这玩意什么意思。想要明白这东西你要先知道文本在Android
中是如何进行存储和计算的。在Android
中文本的绘制需要使用一个bitmap
作为单个字符的缓存,既然是缓存必定要使用一定的空间,我们可以通过setLinearText (true)
告诉Android
我们不需要这样的文本缓存。 - setFakeBoldText (boolean fakeBoldText)
设置文本仿粗体 - measureText (String text)
- measureText (CharSequence text, int start, int end)
- measureText (String text, int start, int end)
- measureText (char[] text, int index, int count)
测量文本宽度,上面我们已经使用过了,这四个方法都是一样的只是参数稍有不同罢了。
三、Typeface中的方法
- defaultFromStyle(int style)
最简单的,简而言之就是把上面所说的四种Style
封装成Typeface
。传入的参数是:BOLD(加粗)
,BOLD_ITALIC(加粗并倾斜)
,ITALIC(倾斜)
,NORMAL(正常)
mTextPaint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
- create(String familyName, int style)
- create(Typeface family, int style)
textPaint.setTypeface(Typeface.create("SERIF", Typeface.NORMAL));
textPaint.setTypeface(Typeface.create(Typeface.SERIF, Typeface.NORMAL));
这两个方法执行的效果完全一样。
- createFromAsset(AssetManager mgr, String path)
- createFromFile(String path)
- createFromFile(File path)
这三者也是一样的,它们都允许我们使用自己的字体比如我们从asset
目录读取一个字体文件。下面是一个简单的例子:
// 获取字体并设置画笔字体
Typeface typeface = Typeface.createFromAsset(context.getAssets(), "kt.ttf");
textPaint.setTypeface(typeface);
3.2 扩展到TextView
说到文本大家第一时间想到的应该是TextView
,其实在TextView
里我们依然可以找到上面很多方法的影子,比如我们可以从TextView
中获取到TextPaint
:
TextPaint paint = mTextView.getPaint();
当然也可以设置TextView
的字体等等:
Typeface typeface = Typeface.createFromAsset(getAssets(), "kt.ttf");
mTextView.setTypeface(typeface);
Android深入理解文字绘制:FontMetrics字体测量及其TextPaint介绍相关推荐
- android 设置Spinner文字标题颜色 字体大小样式
原文地址为: android 设置Spinner文字标题颜色 字体大小样式 // 在初始化之前改变 Spinner文字标题颜色 mySpinner.xml 样式文件放在main.xml ...
- Android之TextView文字绘制流程
一:TextView的onDraw()方法: 1.第一句restartMarqueeIfNeeded()绘制字幕滚动. protected void onDraw(Canvas canvas) {re ...
- android drawtext 方法,Android 文字绘制(DrawText)技术总结
这里的绘制文字不是直接调用TextView.setText(String content)去展示文字内容.而是在View上面通过 canvas.drawText(text, x, y,textPain ...
- android开发 之 Canvas绘制文字,图片
一.Canvas的常用操作速查表 操作类型 相关API 备注 绘制颜色 drawColor, drawRGB, drawARGB 使用单一颜色填充整个画布 绘制基本形状 drawPoint, draw ...
- android自定义控件绘制位置,Android自定义控件之——文字圆形边框(将文字绘制在圆中间)...
自定义的控件写了很多跟圆形有关系的,有时候蛮纠结在圆里面画文字的 有两种思路,一种是画圆之后再画字体,将字体控制在居中的位置 另外一种是:重写TextView,控制TextView的gravity居中 ...
- R语言使用pheatmap绘制热力图(数据归一化、行列聚类、注释、文字角度、字体)
R语言使用pheatmap绘制热力图(数据归一化.行列聚类.注释.文字角度.字体) 目录
- 【Android 应用开发】UI绘制流程 ( 生命周期机制 | 布局加载机制 | UI 绘制流程 | 布局测量 | 布局摆放 | 组件绘制 | 瀑布流布局案例 )
文章目录 一. 博客相关资料 及 下载地址 1. 代码查看方法 ( ① 直接获取代码 | ② JAR 包替换 ) 2. 本博客涉及到的源码查看说明 二. Activity 生命周期回调机制 1. An ...
- Android O: View的绘制流程(二):测量
在前一篇博客Android O: View的绘制流程(一): 创建和加载中, 我们分析了系统创建和加载View的过程,这部分内容完成了View绘制的前置工作. 本文开始分析View的测量的流程. 一 ...
- Android TextView设置部分文字的颜色字体和大小
目录 一,通过Html实现 1,例如实现如下效果 2,为一段文字中某些字单独设置字体大小 3,设置字体 二,通过Span 相关知识 前言 有时候一个TextView显示很多文字,所有文字颜色,字体,大 ...
最新文章
- Java生成html为pdf
- 那些年我们一起玩DIY总结出的经验——网络篇
- Airbnb个性化搜索服务架构
- 《人月神话》(P11)为舍弃而计划
- Silverlight开发常见的一个小问题:2103错误
- 武侠乂怎么修改服务器,武侠乂怎么操作 按键功能详细介绍
- 塞雷三分钟漫画中国史1
- binlog2sql快速闪回
- 从 160 万到 1.5 亿美元 ,开源软件迎来融资热潮
- 程序包com.wonhyoo.common.entity不存在, 找不到符号
- 为什么说中小学编程教育是创新思维体操
- 在LUAT中使用MQTT客户端
- C++编译器优化:Copy Elision(省略不必要的拷贝)
- java 函数式接口与Lambda表达式
- RPC(管理端口的服务)NFS软件 NFS配置文件 简单介绍
- 迅为RK3588开发板Linux安卓12瑞芯微ARM核心板人工智能工业AI主板
- 蔚来汽车笔试题-20210718
- PyG异质图神经网络NotImplementedError问题
- 如何制作 ChatGPT 清晰有效咒语与Chat GPT高效交流——基础篇 第二课
- 计算机软件和软件系统区别,软件就是程序(软件和程序的区别是什么)
热门文章
- FIFA世界杯游戏的操作
- USB OTG插入检测识别
- Genymotion修改Android品牌/型号
- 动态水管流动监测流量分享
- Android断点下载时异常:java.io.IOException: unexpected end of stream,请问该怎么解决
- 比较好的Java 网站/论坛/博客集锦
- echarts 统计图周月切换_如何设置ECharts星期轴的样式
- 中国团队首次描绘出早期肝细胞癌蛋白图谱
- 推荐系统实战 总结一
- Elasticsearch:关于在 Python 中使用 Elasticsearch 你需要知道的一切 - 8.x