Android自定义View(二):Canvas绘制文本

一、Canvas绘制文本相关方法

Canvas里面有以下几类绘制文本的相关方法:

  1. drawText系列,此系列方法为绘制文本的常规方法
drawText(String text, float x, float y, Paint paint) 
drawText(String text, int start, int end, float x, float y, Paint paint)
drawText(CharSequence text, int start, int end, float x, float y,  Paint paint)
drawText(char[] text, int index, int count, float x, float y, Paint paint) 
  1. drawPosText系列,此系列已经被标记为Deprecated了,仅作了解。此系列方法可以把文本的每个字符绘制在特定的位置
drawPosText(char[] text, int index, int count, @Size(multiple = 2) float[] pos, Paint paint)
drawPosText(String text, @Size(multiple = 2) float[] pos, Paint paint) 
  1. drawTextOnPath系列,按照Path指定路径绘制文本
drawTextOnPath(char[] text, int index, int count, Path path,    float hOffset, float vOffset, Paint paint)
drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) 
  1. drawTextRun系列,API 23新增方法,用于绘制一些特殊语言(形状特异或者方向从右到左等)的文本,比如阿拉伯语,这个我们一般用不到 ,仅作了解。
drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount, float x, float y, boolean isRtl, Paint paint)
drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, Paint paint) 
drawTextRun(MeasuredText text, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, Paint paint)  

我们重点关注第一类和第三类。由于Path还没有讲到,所以本篇只讲述第一类方法。

二、文本的基准线与FontMetrics

仔细看可以发现,第一类的各个方法都包含了两个名为x和y的参数。x是绘制文本原点的横坐标,y是文本绘制的基准线。这里首先看参数y,什么是文本基准线?请看下图
在这里插入图片描述

其中红色的直线就是文本的基准线,它用于测量文本垂直方向的位置。对于特定的字体,只需要指定字体大小和基准线y坐标,就可以确定文本垂直方向的位置。
Canvas中绘制文本的时候,文本大小可以通过Paint.setTextSize进行设置。设置完文字大小后,可以通过Paint.getFontMetrics获取FontMetrics对象。FontMetrics对象包含top、ascent、descent、bottom和leading属性,除leading属性外,其余属性均为图中对应的线y坐标与基准线y坐标差值,在上为负值,在下为正值。各个属性(或者说图中各个直线)含义如下:

  • top:The maximum distance above the baseline for the tallest glyph in the font at a given text size.
  • acent:The recommended distance above the baseline for singled spaced text
  • descent:The recommended distance below the baseline for singled spaced text
  • bottom:The maximum distance below the baseline for the lowest glyph in the font at a given text size
  • leading:The recommended additional space to add between lines of text
    不太懂怎么翻译,直接贴文档解析,大家意会一下。

三、文本的对齐方式

上面说到x是绘制文本原点的横坐标,是不是就是绘制文本的第一个字符的起始坐标?看如下示例:

private val paint = Paint().apply {
    isAntiAlias = true
    strokeWidth = 5f
    color = Color.RED
}

private val textPaint = Paint().apply {
    isAntiAlias = true
    textSize = 100f
    color = Color.BLACK
}

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    textPaint.textAlign = Paint.Align.RIGHT
    canvas.drawText("M", 200f, 200f, textPaint)
    canvas.drawPoint(200f, 200f, paint)

    textPaint.textAlign = Paint.Align.CENTER
    canvas.drawText("M", 300f, 200f, textPaint)
    canvas.drawPoint(300f, 200f, paint)

    textPaint.textAlign = Paint.Align.LEFT
    canvas.drawText("M", 500f, 200f, textPaint)
    canvas.drawPoint(500f, 200f, paint)
}

效果:
在这里插入图片描述

可见,文本对齐方式为右对齐(Paint.Align.RIGHT)的时候,x为文本绘制的终点;居中对齐时为中点;左对齐时才是起点。
另外,细心的同学可能会发现点(x,y)和绘制的文本字符是有一定距离的。这是因为字符两侧都有一定的间隙,否则一段文本的字符就都粘在一起了。

四、Android实现在View居中绘制文本

思路:

  • 根据上文可知,水平方向居中可以使用给Paint设置文本居中对齐来实现;
  • 由于Ascent线和Descent线之间为主要的内容区域,垂直方向居中也就是View的中心点要在Ascent线和Descent线的中间,那么View中线到Bootom线的距离 = (fontMetrics.descent - fontMetrics.ascent) / 2,而基准线到Bottom线的距离为fontMetrics.bottom,所以y = height / 2f + (fontMetrics.descent - fontMetrics.ascent) / 2 - fontMetrics.bottom,其中height为View的高度
    所以实现如下:
private val paint = Paint().apply {
    isAntiAlias = true
    strokeWidth = 5f
    color = Color.RED
}

private val textPaint = Paint().apply {
    isAntiAlias = true
    textSize = 100f
    color = Color.BLACK
}

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    // 假定View的宽高为500px
    setMeasuredDimension(500, 500)
}

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    // 给View一个背景颜色
    canvas.drawColor(Color.CYAN)
    textPaint.textAlign = Paint.Align.CENTER
    val fontMetrics = textPaint.fontMetrics
    val y = height / 2f + (fontMetrics.descent - fontMetrics.ascent) / 2 - fontMetrics.bottom
    canvas.drawText("M", width / 2f, y, textPaint)
    canvas.drawPoint(width / 2f, height / 2f, paint)
}

效果:
在这里插入图片描述

另外如果要在绘制文本之前预先测量文本宽度,可以使用Paint.measureText相关方法。

五、使用字体

如果不想使用默认的字体,我们可以通过Paint.setTypeface方法来设置我们想要的字体。Android为我们预置了如下几种字体:
在这里插入图片描述
如果想使用外部字体文件,可以把目标字体文件复制到assets目录下,然后:

private val textPaint = Paint().apply {
    ...
    typeface = Typeface.createFromAsset(context.assets, "Marsboy-Regular.ttf")
}

六、其他

Android中关于绘制字体还有其他非常多的设置,比如说下划线、删除线等等。Paint还有一个TextPaint子类,针对文本提供了一些额外的设置,有兴趣的同学可以了解一下。
Android的API太多,这里无法一一列举额。我们也没有必要死记,大概知道怎么回事就行了,用到的时候看看文档或者借助百度Google搜索一下就好啦。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值