web-dev-qa-db-fra.com

Comment aligner le texte verticalement?

Cible: Android> = 1,6 sur un canevas pur.

Supposons que je veuille écrire une fonction qui dessine un grand rectangle rouge (largeur, hauteur), puis un texte noir Hello World à l'intérieur. Je veux que le texte soit visuellement au centre du rectangle. Alors essayons:

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);
}

Maintenant, je ne sais pas quoi mettre dans l'argument de drawText marqué par ????, c'est-à-dire que je ne sais pas comment aligner verticalement le texte. 

Quelque chose comme

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

semble fonctionner plus ou moins bien, mais il doit y avoir un meilleur moyen.

42
Leszek

Exemple pour centrer sur cx et 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);
}

Pourquoi height()/2f ne fonctionne-t-il pas de la même manière?

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

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

Cela ne donnerait le même résultat que lorsque top est 0. Cela peut être le cas pour certaines polices de toutes tailles ou d'autres polices de certaines tailles, mais pas pour toutes les polices de toutes tailles.

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

La distance entre "base" et "centre" devrait être -(mPaint.descent() + mPaint.ascent()) / 2

25
QQchurch

Basé sur la réponse de steelbytes, le code mis à jour ressemblerait à ceci:

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);
}
22
gauravjain0102

Comme dessiner du texte en Y signifie que la ligne de base du texte se terminera par une différence de Y pixels par rapport à l'Origine, voici ce que vous devez faire pour centrer du texte dans un rectangle de (width, height) dimensions:

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

Gardez à l'esprit que l'ascension est négative (ce qui explique le signe moins).

Cela ne prend pas en compte la descente, ce qui correspond généralement à ce que vous souhaitez (la remontée correspond généralement à la hauteur des majuscules au-dessus de la ligne de base).

14
Pierre-Luc Paour

en utilisant mPaint.getTextBounds (), vous pouvez demander quelle sera la taille du texte une fois dessiné, puis en utilisant cette information, vous pourrez calculer où vous voulez le dessiner.

10
SteelBytes
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);
}

Usage:

PointF p = getTextCenterToDraw(text, rect, Paint);
canvas.drawText(text, p.x, p.y, Paint);
7
Exterminator13

Je suis tombé sur cette question en essayant de résoudre mon problème, et la réponse de @ Weston me convient parfaitement.

Dans le cas de 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);
}
1
N. Osil

Voici une méthode d’extension SkiaSharp C # pour ceux qui le cherchent

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);
}
0
Benoit Jadinon