Как выровнять текст по вертикали?
Цель: 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;
похоже, работает более или менее нормально, но должен быть лучший способ.
Ответы
Ответ 1
Пример для центра на 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
. Это может иметь место для некоторых шрифтов любого размера или других шрифтов с некоторыми размерами, но не для всех шрифтов любого размера.
Ответ 2
textY = topLeftY + height/2 - (mPaint.descent() + mPaint.ascent()) / 2
Расстояние от "базовой линии" до "центра" должно быть -(mPaint.descent() + mPaint.ascent()) / 2
Ответ 3
На основе ответа 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);
}
Ответ 4
Так как рисование текста в Y означает, что текст базовой линии будет заканчиваться Y пикселями вниз от начала координат, что вам нужно сделать, когда вы хотите по центру текста в прямоугольнике (width, height)
размеры:
paint.setTextAlign(Paint.Align.CENTER); // centers horizontally
canvas.drawText(text, width / 2, (height - paint.ascent()) / 2, paint);
Имейте в виду, что восхождение отрицательное (что объясняет знак минуса).
Это не учитывает спуск, обычно это то, что вы хотите (восхождение, как правило, высоты шапки выше базовой линии).
Ответ 5
используя mPaint.getTextBounds(), вы можете спросить, насколько велика будет текст при рисовании, а затем используя эту информацию, которую вы можете вычислить, где вы хотите ее нарисовать.
Ответ 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);
Ответ 7
Я наткнулся на этот вопрос, пытаясь решить мою проблему, и ответ @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);
}
Ответ 8
Вот метод расширения SkiaSharp С# для всех, кто его ищет
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);
}
Ответ 9
Я знаю, что это довольно старый, но это сработало для меня. В основном создайте SKPath для текста, затем используйте высоту от этого. Таким образом, он фактически измеряет точный текст, а не только шрифт в целом.
SKImageInfo info = e.Info;
...
using (SKPaint textPaint = new SKPaint())
{
textPaint.TextSize = 40;
textPaint.Shader = SKShader.CreateColor(SKColors.White);
textPaint.BlendMode = SKBlendMode.SrcOver;
textPaint.IsAntialias = true;
float maxHeight = (textPaint.FontMetrics.Top * -1);
float length = textPaint.MeasureText(Text);
float leftPos = info.Width / 2 - length / 2;
SKPath textPath = textPaint.GetTextPath(Text, 0, 0);
float topPos = info.Height / 2 + textPath.Bounds.Height / 2;
canvas.DrawText(Text, new SKPoint(leftPos, topPos), textPaint);
}