1. 落笔缘由
由于要实现类似注册表单一样,文字作用两端对齐的效果如下图1,所以才有下面的内容。
在canvas中,调用drawText绘画文字的时候,希望能够在垂直方向居中画出文字。所以需要测量出要绘画的文字的高。但具体来说,也不是获取文字的高,因为如果要垂直居中画出文字,就必须拿到文字的baseline,于是在网上搜索,找到了一篇关于获取baseline的文章(文章链接在本文底部列出),感觉就是我想要的,但是基于要怀疑一切的态度,不能你说是什么就是什么的,实践才是检验真理的唯一标准,所以下面我们来验证一下几个观点。
(1) 字体的5个位置是由使用的字体和字号决定的
首先可以明确,5个位置分别是top,ascent,baseline,descent,bottom。这五个位置受字号的大小影响是毋庸置疑的,这里不再讨论。这里验证了一下字体对五个位置的影响。字号默认是16,字体分别是系统默认,行书,小篆,方正姚体:
这5个位置分别用5条不同颜色的横线条展示
- top:浅灰色
- ascent:黄色
- baseline:红色
- descent:蓝色
- bottom:绿色
1)代码展示
private void init()
{
bodyLayout = new LinearLayout(this);
if (bodyLayout != null)
{
llParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT);
if (llParams!=null)
{
bodyLayout.setLayoutParams(llParams);
}
bodyLayout.setOrientation(LinearLayout.VERTICAL);
bodyLayout.setBackgroundColor(Color.WHITE);
//系统默认字体
MeasureText measureText = new MeasureText(this);
if (measureText!=null)
{
llParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
Util.dip2px(this, 200));
if (llParams!=null)
{
measureText.setLayoutParams(llParams);
}
bodyLayout.addView(measureText);
}
//行书
MeasureText measureText2 = new MeasureText(this);
if (measureText2!=null)
{
llParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
Util.dip2px(this, 200));
if (llParams!=null)
{
measureText2.setLayoutParams(llParams);
}
Typeface face = Typeface.createFromAsset (getAssets() , "fonts/xs.ttf" );
measureText2.setFace(face);
bodyLayout.addView(measureText2);
}
//小篆
MeasureText measureText3 = new MeasureText(this);
if (measureText3!=null)
{
llParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
Util.dip2px(this, 200));
if (llParams!=null)
{
measureText3.setLayoutParams(llParams);
}
Typeface face = Typeface.createFromAsset (getAssets() , "fonts/fzxz.TTF" );
measureText3.setFace(face);
bodyLayout.addView(measureText3);
}
//方正姚体
MeasureText measureText4 = new MeasureText(this);
if (measureText4!=null)
{
llParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
Util.dip2px(this, 200));
if (llParams!=null)
{
measureText4.setLayoutParams(llParams);
}
Typeface face = Typeface.createFromAsset (getAssets() , "fonts/fzyt.TTF" );
measureText4.setFace(face);
bodyLayout.addView(measureText4);
}
}
}
2)效果演示
3)结果
从效果看,字体对top,ascent,baseline,descent,bottom的位置影响并不明显。
(2)以baseline为基准,向上为负,向下为正。ascent为负数,descent为正数。
通过Log打印的日志可以得出结果
结果:以baseline为基准线,向上为负,向下为正是正确的。
(3)如果尝试将两个TextView上下排列,没有margin和padding,两个TextView文字之间依然有空隙。首先我们需要设置includeFontPadding为false!但是依然有空隙,这时的空隙就是由top与ascent之间的空隙和bottom与descent直接的空隙造成的了。这个就不用例子验证了,不然怎么解释这个空隙的存在。
(4)baseline = (mHeight - (mFontMetricsInt.descent - mFontMetricsInt.ascent)) / 2 - mFontMetricsInt.ascent
这个不是验证了,说了那么多也就是为了如何在垂直方向居中绘制文字,关键在于获取baseline位置。Canvas中的drawText中绘制文字的基准线是baseline。
baseline = (mHeight - (mFontMetricsInt.descent - mFontMetricsInt.ascent)) / 2 - mFontMetricsInt.ascent
使得ascent到View的是上边距与descent到View下边距距离一致即可,此段距离加上ascent的绝对值(-ascent)即为baseline的位置。
2. 总结
在Canvas中使用drawText绘制垂直居中文字的关键在于求出baseline的位置。获取位置的公式:
baseline = (mHeight - (mFontMetricsInt.descent -
mFontMetricsInt.ascent)) / 2 - mFontMetricsInt.ascent
即baseline = (mHeight - mFontMetricsInt.ascent -mFontMetricsInt.descent)
/ 2
3. 源码地址
https://github.com/lgygg/MeasureHeight