如何垂直对齐文本?

时间:2011-02-05 20:10:09

标签: java android canvas drawtext

目标:纯画布上的Android> = 1.6。

假设我想编写一个绘制(宽度,高度)大红色矩形的函数,然后在里面绘制一个黑色 Hello World 文本。我希望文本在视觉上位于矩形的中心。所以让我们试试:

void drawHelloRectangle(Canvas c, int topLeftX, 
        int topLeftY, int width, int height) {
    Paint mPaint = new Paint();
    // height of 'Hello World'; height*0.7 looks good
    int fontHeight = (int)(height*0.7);

    mPaint.setColor(COLOR_RED);
    mPaint.setStyle(Style.FILL);
    c.drawRect( topLeftX, topLeftY, topLeftX+width, topLeftY+height, mPaint);

    mPaint.setTextSize(fontHeight);
    mPaint.setColor(COLOR_BLACK);
    mPaint.setTextAlign(Align.CENTER);
    c.drawText( "Hello World", topLeftX+width/2, ????, mPaint);
}

现在我不知道在????标记的drawText参数中放什么,即我不知道如何垂直对齐文本。

这样的东西
  

???? = topLeftY + height / 2 +   fontHeight / 2 - fontHeight / 8;

似乎或多或少有效,但必须有更好的方法。

9 个答案:

答案 0 :(得分:99)

cxcy为中心的示例:

private final Rect textBounds = new Rect(); //don't new this up in a draw method

public void drawTextCentred(Canvas canvas, Paint paint, String text, float cx, float cy){
  paint.getTextBounds(text, 0, text.length(), textBounds);
  canvas.drawText(text, cx - textBounds.exactCenterX(), cy - textBounds.exactCenterY(), paint);
}

为什么height()/2f不能正常工作?

exactCentre() = (top + bottom) / 2f

height()/2f = (bottom - top) / 2f

top0时,这些结果只会产生相同的结果。对于某些尺寸的某些字体或某些尺寸的其他字体,可能会出现这种情况,但不适用于所有尺寸的所有字体。

答案 1 :(得分:25)

textY = topLeftY + height/2 - (mPaint.descent() + mPaint.ascent()) / 2

从“基线”到“中心”的距离应为-(mPaint.descent() + mPaint.ascent()) / 2

答案 2 :(得分:21)

根据steelbytes的回复,更新后的代码如下所示:

void drawHelloRectangle(Canvas c, int topLeftX, int topLeftY, int width, int height) {
    Paint mPaint = new Paint();
    // height of 'Hello World'; height*0.7 looks good
    int fontHeight = (int)(height*0.7);

    mPaint.setColor(COLOR_RED);
    mPaint.setStyle(Style.FILL);
    c.drawRect( topLeftX, topLeftY, topLeftX+width, topLeftY+height, mPaint);

    mPaint.setTextSize(fontHeight);
    mPaint.setColor(COLOR_BLACK);
    mPaint.setTextAlign(Align.CENTER);
    String textToDraw = new String("Hello World");
    Rect bounds = new Rect();
    mPaint.getTextBounds(textToDraw, 0, textToDraw.length(), bounds);
    c.drawText(textToDraw, topLeftX+width/2, topLeftY+height/2+(bounds.bottom-bounds.top)/2, mPaint);
}

答案 3 :(得分:15)

由于在Y处绘制文本意味着文本的基线将从原点开始向下Y像素,因此当您希望将文本置于{{1的矩形中心时,需要执行的操作维度是:

(width, height)

请记住,上升是负数(这解释了减号)。

这不考虑下降,这通常是你想要的(上升通常是高于基线的上限的高度)。

答案 4 :(得分:9)

使用mPaint.getTextBounds(),您可以询问绘制时文本的大小,然后使用该信息计算您想要绘制的文本。

答案 5 :(得分:7)

public static PointF getTextCenterToDraw(String text, RectF region, Paint paint) {
    Rect textBounds = new Rect();
    paint.getTextBounds(text, 0, text.length(), textBounds);
    float x = region.centerX() - textBounds.width() * 0.4f;
    float y = region.centerY() + textBounds.height() * 0.4f;
    return new PointF(x, y);
}

用法:

PointF p = getTextCenterToDraw(text, rect, paint);
canvas.drawText(text, p.x, p.y, paint);

答案 6 :(得分:1)

在试图解决我的问题时我偶然发现了这个问题,@ Weston的答案对我来说很好。

对于Kotlin:

private fun drawText(canvas: Canvas) {
    paint.textSize = 80f
    val text = "Hello!"
    val textBounds = Rect()
    paint.getTextBounds(text, 0, text.length, textBounds);
    canvas.drawText(text, cx- textBounds.exactCenterX(), cy - textBounds.exactCenterY(), paint);
    //in case of another Rect as a container:
    //canvas.drawText(text, containerRect.exactCenterX()- textBounds.exactCenterX(), containerRect.exactCenterY() - textBounds.exactCenterY(), paint);
}

答案 7 :(得分:0)

这是SkiaSharp C#扩展方法,适用于任何寻求它的人

public static void DrawTextCenteredVertically(this SKCanvas canvas, string text, SKPaint paint, SKPoint point)
{
    var textY = point.Y + (((-paint.FontMetrics.Ascent + paint.FontMetrics.Descent) / 2) - paint.FontMetrics.Descent);
    canvas.DrawText(text, point.X, textY, paint);
}

答案 8 :(得分:0)

private final Rect textBounds = new Rect();//No need o create again

public void drawTextCentred(Canvas canvas, Paint paint, String text, float cx, float cy, float desiredWidth) {
    final float testTextSize = 48f;
    paint.setTextSize(testTextSize);
    Rect bounds = new Rect();
    paint.getTextBounds(text, 0, text.length(), bounds);
    float desiredTextSize = testTextSize * desiredWidth / bounds.width();
    paint.setTextSize(desiredTextSize);
    paint.getTextBounds(text, 0, text.length(), textBounds);
    paint.setTextAlign(Paint.Align.CENTER);
    canvas.drawText(text, cx, cy - textBounds.exactCenterY(), paint);
}