50

目标:纯 Canvas 上的 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;

似乎或多或少可以工作,但必须有更好的方法。

4

9 回答 9

119

cx以and为中心的示例cy

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

这些只会在topis时产生相同的结果0。这可能适用于所有大小的某些字体或某些大小的其他字体,但并非适用于所有大小的所有字体。

于 2014-07-26T09:47:06.143 回答
29
textY = topLeftY + height/2 - (mPaint.descent() + mPaint.ascent()) / 2

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

于 2012-04-21T01:48:08.523 回答
22

根据 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);
}
于 2011-12-18T17:58:48.417 回答
16

由于在 Y 处绘制文本意味着文本的基线将从原点向下 Y 像素,因此当您想要在(width, height)尺寸矩形内居中文本时需要做的是:

paint.setTextAlign(Paint.Align.CENTER);  // centers horizontally
canvas.drawText(text, width / 2, (height - paint.ascent()) / 2, paint);

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

这没有考虑下降,这通常是您想要的(上升通常是基线上方的大写高度)。

于 2013-04-16T12:53:22.043 回答
9

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

于 2011-02-06T02:59:30.667 回答
6
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);
于 2012-07-18T23:11:17.523 回答
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);
}
于 2018-05-02T16:41:54.680 回答
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);
}
于 2018-08-15T15:53:47.897 回答
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);
}
于 2021-03-10T08:35:42.260 回答