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是怎么绘制文字的,一会我们要做一个实际的例子来巩固巩固。首先,我们要先来扩展认识两个方法:

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.

一句话解释:就是得到上坡度的值

实际代码:

@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来绘制文字的更多相关文章

  1. Android 使用View绘制文字(DrawText)技术总结

    转载请注明出处: http://www.cnblogs.com/renhui/p/7453534.html 这里的绘制文字不是直接调用TextView.setText(String content)去 ...

  2. Android 使用Canvas在图片上绘制文字

    一个小应用,在图片上绘制文字,以下是绘制文字的方法,并且能够实现自动换行,字体自动适配屏幕大小 private void drawNewBitmap(ImageView imageView, Stri ...

  3. 【朝花夕拾】Android自定义View篇之(三)Canvas绘制文字

    前言 转载请声明,转自[https://www.cnblogs.com/andy-songwei/p/10968358.html],谢谢! 前面的文章中在介绍Canvas的时候,提到过后续单独讲Can ...

  4. C#+OpenGL+FreeType显示3D文字(3) - 用PointSprite绘制文字

    C#+OpenGL+FreeType显示3D文字(3) - 用PointSprite绘制文字 上一篇实现了把文字绘制到OpenGL窗口,但实质上只是把含有文字的贴图贴到矩形模型上.本篇我们介绍用Poi ...

  5. C#+OpenGL+FreeType显示3D文字(2) - 用GLSL+VBO绘制文字

    C#+OpenGL+FreeType显示3D文字(2) - 用GLSL+VBO绘制文字 +BIT祝威+悄悄在此留下版了个权的信息说: 上一篇得到了字形贴图及其位置字典(可导出为XML).本篇就利用此贴 ...

  6. IOS开发 图形绘制,绘制线条,矩形,和垂直和居中绘制文字

    概述 吐槽下IOS下 的图形绘图,代码冗长,不得不自己重新封装方法.整理形成本文. 绘制线 // 绘制直线 + (void)toDrawLineFromX:(CGFloat)x1 Y:(CGFloat ...

  7. Canvas入门(3):图像处理和绘制文字

    来源:http://www.ido321.com/997.html 一.图像处理(非特别说明,所有结果均来自最新版Google) 在HTML 5中,不仅可以使用Canvas API绘制图形,也可以用于 ...

  8. 在Image控件中绘制文字

    //Canvas 在Image控件中绘制文字 procedure TForm1.Button1Click(Sender: TObject);begin  image1.Canvas.Font.Size ...

  9. 12-UIKit(View绘制、绘制曲线、绘制文字、贴图)

    目录: 1. View绘制 2. 绘制曲线 3. 绘制文字 4. 贴图 回到顶部 1. View绘制 1.1 做出自己的视图对象 TRCell : UITableViewCell : UIView U ...

随机推荐

  1. 在redhat上搭建redmine

    搞个项目管理的东西 找了下还是redmine比较合适,行动action: 1.ruby 额 是的你没有看错 需要先安装一个ruby的环境.话说这个安装起来很是纠结,本来想用yum 结果咩有成功,于是乎 ...

  2. 基于jQuery HTML5人物介绍卡片特效

    基于jQuery HTML5人物介绍卡片特效.这是一款基于jquery.material-cards插件实现的人物介绍卡片形式特效代码.效果图如下: 在线预览   源码下载 实现的代码. html代码 ...

  3. MyBatis知多少(3)

    解决存储过程固有限制的方法之一就是将SQL嵌入到更加通用的语言中去.与存储过程将业务逻辑移入数据库相反,内联SQL将SQL从数据库移入了应用程序代码.这就使得SQL语句可以直接与语言进行交互.从某种意 ...

  4. [转]SpringMVC Controller&View数据传递

    Spring MVC3在controller和视图之间传递参数的方法:   一, 从controller往视图传递值, controller---->视图   1)简单类型,如int, Stri ...

  5. php和egret的配合

    egret对资源路径和js的应用都是相对路径,而在现在许多流行的框架里,一般都把js和资源放到专门的文件夹下,如public. 修改步骤: 1.修改index.html,改为全路径,如: <sc ...

  6. tcp为什要三次握手

    准备知识: 单工:信息只能单向传递.发送-->接收,单向,不能返回响应. 双工:指的是信息可双向发送. 全双工:信息可同时双向传递. 半双工:不能同时,单行道,一边传输完了,另一边才能发起传输. ...

  7. Direct3D11学习:(一)开发环境配置

    转载请注明出处:http://www.cnblogs.com/Ray1024   从今天开始,开启一个新的系列:Direct3D11的学习教程. 因为一直对3D方面比较感兴趣,最近决定开始学习D3D知 ...

  8. EPANET能做什么,不能做什么

    What Epanet cand and cannot do Good news!Epanet can do most of the calculations you may need for you ...

  9. Microsoft Visual Studio 2012 文档 下载地址 vs2012 中文帮助文档

    https://www.microsoft.com/zh-cn/download/confirmation.aspx?id=34794 下载地址: http://download.microsoft. ...

  10. Winform开发框架之客户关系管理系统(CRM)的开发总结系列4-Tab控件页面的动态加载

    在前面介绍的几篇关于CRM系统的开发随笔中,里面都整合了多个页面的功能,包括多文档界面,以及客户相关信息的页面展示,这个模块就是利用DevExpress控件的XtraTabPage控件的动态加载实现的 ...