2011-02-05 3 views
41

Цель: 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;

похоже, работает более или менее нормально, но должен быть лучший способ.

+18

Каждый раз, когда вы помещаете * Android * в заголовок вопроса, котенок умрет, поэтому прекратите это делать. Благодарю. –

ответ

83

Пример к центру на cx и 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

Они лишь дают тот же результат, когда top является 0. Это может иметь место для некоторых шрифтов любого размера или других шрифтов в некоторых размерах, но не для всех шрифтов любых размеров.

+1

Это должен быть принятый ответ. Мало того, что это самый простой из всех, он единственный, который на самом деле оптически центрирует текст по вертикали, должным образом принимая восхождения и спуски (не в общем, а фактические, присутствующие в фактическом тексте, который будет отображаться). –

+0

Был через множество обручей и петель, чтобы взломать это - и тогда это было так просто. Это _the_ ответ, который вы ищете! – slott

+0

Может кто-нибудь сказать мне, почему textBounds.height()/2 не работает должным образом? (По крайней мере, не для меня.) Я нашел этот ответ и заменил height() на centerY(), и теперь он работает ... – bwoogie

9

используя mPaint.getTextBounds(), вы можете спросить, насколько велика будет текст при рисовании, а затем используя эту информацию, вы можете вычислить, где вы хотите ее нарисовать.

+2

getTextBounds() принадлежит Paint, а не Canvas – Melllvar

+4

Помните, что когда вы говорите вертикально центрированный текст, вы, вероятно, имеете в виду восходящую часть символа, а не базовую линию. Когда вы рисуете с помощью Align.CENTER, координата Y будет центрировать на базовой линии символа. –

+0

@CameronLowellPalmer Не могли бы вы подробнее рассказать об этом? – Peterdk

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); 
} 
24
textY = topLeftY + height/2 - (mPaint.descent() + mPaint.ascent())/2 

Расстояние от "базового" до "центра" должен быть -(mPaint.descent() + mPaint.ascent())/2

+0

Это не сработало для меня, но принятый ответ работал отлично. Возможно, этот результат выглядит лучше, если шрифт меньше. – spaaarky21

5
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); 
+9

Почему это * 0.4f? –

+0

Отличный пример, он работает для меня :) +1 –

13

С рисования текста на Y означает, что базовый текста закончится Y пикселов вниз от начала координат, что вам нужно сделать, если вы хотите, чтобы центрировать текст внутри прямоугольника (width, height) размерностей:

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

Имейте в виду, что восхождение отрицательное (что объясняет знак минус).

Это не учитывает спуск, что обычно является тем, что вы хотите (восхождение, как правило, высоты крышек над базовой линией).

Смежные вопросы