用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处开始的,Baseline往上至字符“最高处”的距离我们称之为ascent(上坡度),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卡了ξτβбпшㄎㄊěǔぬも┰┠№@↓";
@Override
protected 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是怎么绘制文字的,一会我们要做一个实际的例子来巩固巩固。首先,我们要先来扩展认识两个方法:
解释:the distance below (positive) the baseline (descent) based on the current typeface and text size.一句话解释:得到下坡度的值解释:the distance above (negative) the baseline (ascent) based on the current typeface and text size.
一句话解释:就是得到上坡度的值
实际代码:
@Override
protected 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()
得到下坡度的值
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宽度的一半减去文本宽度的一半:
@Override
protected 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);
说明:本文大部分内容来自:http://blog.csdn.net/aigestudio/article/details/41447349,我对原文进行了少量修改,记录在此。
From AigeStudio(http://blog.csdn.net/aigestudio)Power by Aige 尊重原作者,感谢作者的分享!
用TextPaint来绘制文字的更多相关文章
- Android 使用View绘制文字(DrawText)技术总结
转载请注明出处: http://www.cnblogs.com/renhui/p/7453534.html 这里的绘制文字不是直接调用TextView.setText(String content)去 ...
- Android 使用Canvas在图片上绘制文字
一个小应用,在图片上绘制文字,以下是绘制文字的方法,并且能够实现自动换行,字体自动适配屏幕大小 private void drawNewBitmap(ImageView imageView, Stri ...
- 【朝花夕拾】Android自定义View篇之(三)Canvas绘制文字
前言 转载请声明,转自[https://www.cnblogs.com/andy-songwei/p/10968358.html],谢谢! 前面的文章中在介绍Canvas的时候,提到过后续单独讲Can ...
- C#+OpenGL+FreeType显示3D文字(3) - 用PointSprite绘制文字
C#+OpenGL+FreeType显示3D文字(3) - 用PointSprite绘制文字 上一篇实现了把文字绘制到OpenGL窗口,但实质上只是把含有文字的贴图贴到矩形模型上.本篇我们介绍用Poi ...
- C#+OpenGL+FreeType显示3D文字(2) - 用GLSL+VBO绘制文字
C#+OpenGL+FreeType显示3D文字(2) - 用GLSL+VBO绘制文字 +BIT祝威+悄悄在此留下版了个权的信息说: 上一篇得到了字形贴图及其位置字典(可导出为XML).本篇就利用此贴 ...
- IOS开发 图形绘制,绘制线条,矩形,和垂直和居中绘制文字
概述 吐槽下IOS下 的图形绘图,代码冗长,不得不自己重新封装方法.整理形成本文. 绘制线 // 绘制直线 + (void)toDrawLineFromX:(CGFloat)x1 Y:(CGFloat ...
- Canvas入门(3):图像处理和绘制文字
来源:http://www.ido321.com/997.html 一.图像处理(非特别说明,所有结果均来自最新版Google) 在HTML 5中,不仅可以使用Canvas API绘制图形,也可以用于 ...
- 在Image控件中绘制文字
//Canvas 在Image控件中绘制文字 procedure TForm1.Button1Click(Sender: TObject);begin image1.Canvas.Font.Size ...
- 12-UIKit(View绘制、绘制曲线、绘制文字、贴图)
目录: 1. View绘制 2. 绘制曲线 3. 绘制文字 4. 贴图 回到顶部 1. View绘制 1.1 做出自己的视图对象 TRCell : UITableViewCell : UIView U ...
随机推荐
- React Native Android增加本地图片
将图片文件 UePbdph.png 放入与index.android.js的同目录中,在index.android.js中引入: <Image source={require('./UePbdp ...
- Material Design UI Widgets
Android L 开发者预览支持库提供两个新的Widgets,RecyclerView和CardView.使用这两个Widgets可以显示复杂的Listview和卡片布局,这两个Widgets默认使 ...
- 【Machine Learning】机器学习の特征
绘制了一张导图,有不对的地方欢迎指正: 下载地址 机器学习中,特征是很关键的.其中包括,特征的提取和特征的选择.他们是降维的两种方法,但又有所不同: 特征抽取(Feature Extraction): ...
- css省略号布局实例截图
过多文字li标签出现使用css省略号样式截图 使用text-overflow样式让显示不完内容通过css实现省略号排版
- js获取html5 audio 音频时长方法
<audio src="我的好兄弟.mp3" controls="controls" id="audio" style=" ...
- C# 只启动一个实例完全解决方案
工作上经常会遇到"程序只能启动一个实例"这样的需求. 我想,这样的需求应该很普遍,所以没打算去动脑筋,去找谷歌问下就得了,用下来发现,不是这里不爽就是那里不行. 先说下我详细的几点 ...
- Blocked Process Report
当同个对象上有互斥的锁存在时,查询需要等待很长时间,我们是否可以收到来自SQL Server提醒?答案是可以的,做法非常简单,因为SQL Server为你提供了称为Blocked Process Re ...
- js-二维数组和多维数组
一.二维数组的表示 myarray[][] 二.二维数组的定义 方法一: var a = new Array(); for(var i=0;i<3;i++){ //一维长度为3 a[i] = n ...
- 【转】Bloom Filter布隆过滤器的概念和原理
转自:http://blog.csdn.net/jiaomeng/article/details/1495500 之前看数学之美丽,里面有提到布隆过滤器的过滤垃圾邮件,感觉到何其的牛,竟然有这么高效的 ...
- 在aspx怎么引用public string getPicurl(string picurl)?
刚才在论坛上看到一帖: Insus.NET尝试做了一下,直接使用一个Img标签是无法实现.因为函数中返回的即是一个img html标签,因此在aspx页再不能使用Img了. 现在可以回到网友的问题,那 ...