测量在Canvas(Android)上绘制的文本高度

Dan*_*edo 119 graphics android view android-canvas

有没有直接测量文字高度的方法?我现在这样做的方法是使用Paint measureText()来获取宽度,然后通过试验和错误找到一个值来获得近似高度.我也一直在搞乱FontMetrics,但所有这些似乎都是近似的方法.

我正在尝试扩展不同分辨率的东西.我可以做到,但我最终得到了令人难以置信的冗长代码,并通过大量计算来确定相对大小.我讨厌它!一定有更好的方法.

Sur*_*gch 168

根据您的需要,有多种方法可以测量高度.

getTextBounds

如果您正在做一些精确定位少量固定文本的内容,您可能需要getTextBounds.您可以像这样获得边界矩形

Rect bounds = new Rect();
mTextPaint.getTextBounds(mText, 0, mText.length(), bounds);
int height = bounds.height();
Run Code Online (Sandbox Code Playgroud)

正如您可以看到以下图像,不同的字符串将给出不同的高度(以红色显示).

在此输入图像描述

在某些情况下,无论文本是什么,只需要一个恒定的高度,这些不同的高度都可能是一个缺点.请参阅下一节.

Paint.FontMetrics

您可以从字体指标计算字体的高度.高度始终相同,因为它是从字体获得的,而不是任何特定的文本字符串.

Paint.FontMetrics fm = mTextPaint.getFontMetrics();
float height = fm.descent - fm.ascent;
Run Code Online (Sandbox Code Playgroud)

基线是文本所在的行.下降通常是最远的一个角色将在线下方,并且上升通常是最远的一个角色将在线上方.要获得高度,您必须减去上升,因为它是负值.(基线是y=0y减少屏幕.)

请看下面的图片.两个琴弦的高度都是234.375.

在此输入图像描述

如果您想要行高而不仅仅是文本高度,则可以执行以下操作:

float height = fm.bottom - fm.top + fm.leading; // 265.4297
Run Code Online (Sandbox Code Playgroud)

这些是bottomtop线.前导(行间距)通常为零,但无论如何都应该添加它.

上面的图片来自这个项目.您可以使用它来查看Font Metrics的工作原理.

StaticLayout

要测量多行文本的高度,您应该使用a StaticLayout.我在这个答案中详细讨论了它,但获得这个高度的基本方法是这样的:

String text = "This is some text. This is some text. This is some text. This is some text. This is some text. This is some text.";

TextPaint myTextPaint = new TextPaint();
myTextPaint.setAntiAlias(true);
myTextPaint.setTextSize(16 * getResources().getDisplayMetrics().density);
myTextPaint.setColor(0xFF000000);

int width = 200;
Layout.Alignment alignment = Layout.Alignment.ALIGN_NORMAL;
float spacingMultiplier = 1;
float spacingAddition = 0;
boolean includePadding = false;

StaticLayout myStaticLayout = new StaticLayout(text, myTextPaint, width, alignment, spacingMultiplier, spacingAddition, includePadding);

float height = myStaticLayout.getHeight(); 
Run Code Online (Sandbox Code Playgroud)

  • @MichealJohnson,我将应用程序添加为 [GitHub 项目](https://github.com/suragch/AndroidFontMetrics)。 (2认同)
  • 以像素为单位的字体大小与测量高度和 [FontMetrics 尺寸](/sf/answers/1934221621/) 之间的关系是什么?这是我想进一步探讨的问题。 (2认同)

bra*_*amp 128

那么paint.getTextBounds()(对象方法)呢

  • 高度的变化是文本中有下划线的地方,即"高"高于"低",因为g的部分位于线下方 (35认同)
  • 当我评估文本的高度时,这会产生非常奇怪的结果。短文本的高度为 12,而长文本的高度为 16(给定字体大小为 16)。对我来说没有意义(android 2.3.3) (2认同)

Nar*_*Gar 82

@ bramp的答案是正确的 - 部分原因在于它没有提到计算的边界将是包含完全隐含起始坐标0,0的文本的最小矩形.

这意味着,例如"Py"的高度将不同于"py"或"hi"或"oi"或"aw"的高度,因为它们在像素方面需要不同的高度.

这绝不等同于经典java中的FontMetrics.

虽然文字的宽度不是很大,但是高度却是.

特别是,如果您需要垂直居中对齐绘制的文本,请尝试获取文本"a"(不带引号)的边界,而不是使用您要绘制的文本.对我有用......

这就是我的意思:

Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.LINEAR_TEXT_FLAG);

paint.setStyle(Paint.Style.FILL);
paint.setColor(color);
paint.setTextAlign(Paint.Align.CENTER);
paint.setTextSize(textSize);

Rect bounds = new Rect();
paint.getTextBounds("a", 0, 1, bounds);

buffer.drawText(this.myText, canvasWidth >> 1, (canvasHeight + bounds.height()) >> 1, paint);
// remember x >> 1 is equivalent to x / 2, but works much much faster
Run Code Online (Sandbox Code Playgroud)

垂直居中对齐文本意味着垂直居中对齐边界矩形 - 这对于不同的文本(大写字母,长字母等)是不同的.但我们实际想要做的是同时对齐渲染文本的基线,使它们看起来不会升高或凹凸不平.因此,只要我们知道最小字母的中心(例如"a"),我们就可以将其对齐重用于其余的文本.这将使所有文本居中对齐以及基线对齐它们.

  • 一个好的现代编译器将看到`x/2`并将其优化为`x >> 1` (60认同)
  • 考虑到克里斯的评论,@ keaukraine`x/2`在阅读代码时要友好得多. (37认同)
  • 多年没见过`x >> 1`.仅为此提升:) (25认同)

小智 15

高度是您在Paint变量上设置的文本大小.

找出高度的另一种方法是

mPaint.getTextSize();
Run Code Online (Sandbox Code Playgroud)