平时我们在自定义View经常会遇到text的绘制,经常会需要文字水平居中,垂直居中,水平居中比较简单,而垂直居中稍微复杂,所以我们这里讨论下垂直居中。这里涉及到一个关键类Paint.FontMetrics,可以通过Paint实例的getFontMetrics()获得,其中封装几个垂直相关的参数。

ascent:是baseline之上至字符最高处的距离

descent:是baseline之下至字符最低处的距离

top:指的是指的是最高字符到baseline的值,即ascent的最大值

bottom:指的是最下字符到baseline的值,即descent的最大值

当我们在设置paint.setTextSize(80)后,打印日志:

06-24 02:56:54.792 7383-7383/sample.kingja.canvas E/FontMetricsView: top: -83.828125

06-24 02:56:54.792 7383-7383/sample.kingja.canvas E/FontMetricsView: ascent: -74.21875

06-24 02:56:54.792 7383-7383/sample.kingja.canvas E/FontMetricsView: descent: 19.53125

06-24 02:56:54.792 7383-7383/sample.kingja.canvas E/FontMetricsView: bottom: 21.679688

我们发现4个参数有有正数,有负数,根据上面的说明后,现在我们可以确定baseline在里面充当了一个基准线,也就是0位置。于是我们先用以下坐标点作为一个baseline的坐标绘制一段文字。measuredWidth,measuredHeight 为控件的宽,高。

baseX = measuredWidth * 0.5f - textWidth * 0.5f;//textWidth = paint.measureText(text)

baseY = measuredHeight * 0.5f;

为了直观,我们用直线实体化了这几个参数,效果如下:


我们发现文字已经完成了水平居中,这样只要将font middle这条白线降到控件的中间黑线就可以实现垂直居中了,那么只要求出偏移量offsetY就可以了,很明显,font middle到base的距离=文字高度/2-bottom到base的距离

float offsetY=0.5f*fontHeight-bottom;

baseY+=offsetY;

按照新的baseline坐标,我们画出的文字效果图如下:

fontHeight = bottom – top效果图

基本完成了需求,但是由于这个时候我们fontHeight = bottom – top,感觉效果并不是很理想,因为ascent以上到top挺多空隙,而descent到bottom的空隙并不大,这会让人感觉到文字略微偏下,但是Android既然设置了top和bottom,那么文字的高度范围可以被认为在bottom和top之间。所以大家如果想要更理想的效果可把fontHeight设为descent-ascent。我个人觉得,fontHeight = bottom – top这样更稳妥点。当然我也做出了效果,大家可以对比下:

两种计算方式效果图

看得出,虽然差别不多,但是还是有所出入,比如“好”字那个子的横笔画,一个在中线下,一个在中线上。

最后给出两种计算方式的公式:

以fontHeight = bottom – top为文字高度

baseY = measuredHeight * 0.5f+(bottom - top)*0.5f-bottom;

以fontHeight = descent- ascent为文字高度

baseY = measuredHeight * 0.5f+(descent-ascent)*0.5f-descent;

我想,到现在,大家应该比较确定,这是靠谱的垂直居中了吧。