Текст Android Center на полотні


191

Я намагаюся відобразити текст, використовуючи код нижче. Проблема полягає в тому, що текст не по центру горизонтально. Коли я встановлюю координати для drawText, він встановлює нижню частину тексту в цьому положенні. Я хотів би, щоб текст був намальований так, щоб текст був розташований також по центру горизонтально.

Це малюнок для подальшого відображення моєї проблеми:

Знімок екрана

@Override
protected void onDraw(Canvas canvas) {
    // TODO Auto-generated method stub
    super.onDraw(canvas);
    //canvas.drawRGB(2, 2, 200);
    Paint textPaint = new Paint();
    textPaint.setARGB(200, 254, 0, 0);
    textPaint.setTextAlign(Align.CENTER);
    textPaint.setTypeface(font);
    textPaint.setTextSize(300);
    canvas.drawText("Hello", canvas.getWidth()/2, canvas.getHeight()/2  , textPaint);
}

20
Я б не оголосив ваш об’єкт фарби всередині вашої події onDraw. Він відтворюється щоразу, коли його переробляють. Подумайте, як зробити його змінною приватного класу.
Крістофер Ратхеб

8
Мате! Зверніть увагу, що ваше посилання на зображення - NSFW! Я не маю на увазі бути розсудливим, але мені не потрібна реклама з топлес-жінками, що з’являються на моєму екрані в офісі.
Майкл Шепер

5
@MichaelScheper Вибачте, я оновив посилання!
Себастьян

23
@Sebastian Ей, ви все ще отримали це посилання?
С.Лукаш

@ S.Lukas hhahaha
BOTJr.

Відповіді:


378

Спробуйте наступне:

 int xPos = (canvas.getWidth() / 2);
 int yPos = (int) ((canvas.getHeight() / 2) - ((textPaint.descent() + textPaint.ascent()) / 2)) ; 
 //((textPaint.descent() + textPaint.ascent()) / 2) is the distance from the baseline to the center.

 canvas.drawText("Hello", xPos, yPos, textPaint);

10
Чудова відповідь. Для мене я використав наступне, як мені потрібно було повністю центрувати текст горизонтально, а не текст, щоб почати з центрального положення: int xPos = (Width - textPaint.TextSize * Math.Abs ​​(_text.Length / 2)) / 2; Не впевнений, чи є кращий спосіб досягти цього.
paj7777

І, мабуть, найкраще закинути _text.Length до плаваючого тексту, оскільки він, очевидно, не працюватиме для непарної довжини тексту.
paj7777

61
paj7777, це не потрібно, якщо ви встановили textPaint.setTextAlign (Align.CENTER);
Кості Мурару

@ paj7777 Це працювало б лише для шрифтів фіксованої ширини. Крім того, вам не потрібно кидатись на поплавок; якщо розділити на поплавок, результатом буде поплавок. наприклад, float halfLength = text.length() / 2f;це називається просуванням типу .
Майкл Шепер

2
Я порівнював цей підхід (= центр з Paint.descent()і Paint.ascent()) з підходом до тексту центру Paint.getTextBounds()у своїй відповіді нижче. Paint.descent()і Paint.ascent()не враховувати власне текст. (Ви можете розпізнати цю неточність на скріншоті в моїй публікації нижче.) Тому я рекомендую проти такого підходу. Підхід з, Paint.getTextBounds()здається, працює більш точним.
andreas1724

217

Центр з Paint.getTextBounds () :

введіть тут опис зображення

private Rect r = new Rect();

private void drawCenter(Canvas canvas, Paint paint, String text) {
    canvas.getClipBounds(r);
    int cHeight = r.height();
    int cWidth = r.width();
    paint.setTextAlign(Paint.Align.LEFT);
    paint.getTextBounds(text, 0, text.length(), r);
    float x = cWidth / 2f - r.width() / 2f - r.left;
    float y = cHeight / 2f + r.height() / 2f - r.bottom;
    canvas.drawText(text, x, y, paint);
}
  • Paint.Align.CENTER не означає, що опорна точка тексту розташована вертикально. Точка відліку завжди знаходиться на базовій лінії. Отже, чому б не використовувати Paint.Align.LEFT ? Вам доведеться обчислити опорну точку в будь-якому випадку.

  • Paint.descent () має недолік, який не враховує реальний текст. Paint.descent () отримує те саме значення, незалежно від того, чи містить текст букви зі спусками чи ні. Ось чому я замість цього використовую r.bottom .

  • У мене виникли проблеми з Canvas.getHeight (), якщо API <16. Тому я замість цього використовую Canvas.getClipBounds (Rect) . (Не використовуйте Canvas.getClipBounds (). GetHeight (), оскільки він виділяє пам'ять для Rect .)

  • З міркувань продуктивності слід виділити об'єкти перед тим, як вони будуть використані в onDraw () . Оскільки drawCenter () буде викликатися в onDraw (), об'єкт Rect r попередньо виділяється як поле тут.


Я спробував поставити код двох головних відповідей у ​​свій власний код (серпень 2015 року) і зробив скріншот для порівняння результатів:

текст зосереджений у трьох версіях

Текст повинен бути по центру в червоному заповненому прямокутнику. Мій код створює білий текст, інші два коди створюють сірий текст (вони фактично однакові, перекриваються). Сірий текст трохи занизький і два набагато праворуч.

Ось як я зробив тест:

import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

class MyView extends View {

    private static String LABEL = "long";
    private static float TEXT_HEIGHT_RATIO = 0.82f;

    private FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(0, 0);
    private Rect r = new Rect();
    private Paint paint = new Paint();
    private Paint rectPaint = new Paint();

    public MyView(Context context) {
        super(context);
    }

    private void drawTextBounds(Canvas canvas, Rect rect, int x, int y) {
        rectPaint.setColor(Color.rgb(0, 0, 0));
        rectPaint.setStyle(Paint.Style.STROKE);
        rectPaint.setStrokeWidth(3f);
        rect.offset(x, y);
        canvas.drawRect(rect, rectPaint);
    }

    // andreas1724 (white color):
    private void draw1(Canvas canvas, Paint paint, String text) {
        paint.setTextAlign(Paint.Align.LEFT);
        paint.setColor(Color.rgb(255, 255, 255));
        canvas.getClipBounds(r);
        int cHeight = r.height();
        int cWidth = r.width();
        paint.getTextBounds(text, 0, text.length(), r);
        float x = cWidth / 2f - r.width() / 2f - r.left;
        float y = cHeight / 2f + r.height() / 2f - r.bottom;
        canvas.drawText(text, x, y, paint);
        drawTextBounds(canvas, r, (int) x, (int) y);
    }

    // Arun George (light green color):
    private void draw2(Canvas canvas, Paint textPaint, String text) {
        textPaint.setTextAlign(Paint.Align.CENTER);
        textPaint.setColor(Color.argb(100, 0, 255, 0));
        int xPos = (canvas.getWidth() / 2);
        int yPos = (int) ((canvas.getHeight() / 2) - ((textPaint.descent() + textPaint.ascent()) / 2));
        canvas.drawText(text, xPos, yPos, textPaint);
    }

    // VinceStyling (light blue color):
    private void draw3(Canvas yourCanvas, Paint mPaint, String pageTitle) {
        mPaint.setTextAlign(Paint.Align.LEFT);
        mPaint.setColor(Color.argb(100, 0, 0, 255));
        r = yourCanvas.getClipBounds();
        RectF bounds = new RectF(r);
        bounds.right = mPaint.measureText(pageTitle, 0, pageTitle.length());
        bounds.bottom = mPaint.descent() - mPaint.ascent();
        bounds.left += (r.width() - bounds.right) / 2.0f;
        bounds.top += (r.height() - bounds.bottom) / 2.0f;
        yourCanvas.drawText(pageTitle, bounds.left, bounds.top - mPaint.ascent(), mPaint);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        int margin = 10;
        int width = w - 2 * margin;
        int height = h - 2 * margin;
        params.width = width;
        params.height = height;
        params.leftMargin = margin;
        params.topMargin = margin;
        setLayoutParams(params);
        paint.setTextSize(height * TEXT_HEIGHT_RATIO);
        paint.setAntiAlias(true);
        paint.setTypeface(Typeface.create(Typeface.SERIF, Typeface.BOLD_ITALIC));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.rgb(255, 0, 0));
        draw1(canvas, paint, LABEL);
        draw2(canvas, paint, LABEL);
        draw3(canvas, paint, LABEL);
    }
}

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        FrameLayout container = new FrameLayout(this);
        container.setLayoutParams(new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));
        container.addView(new MyView(this));
        setContentView(container);
    }
}

1
Дуже дякую .. Ваша логіка мені дуже допомогла.
Sujay ООН

Дуже дякую .. Ваша логіка теж мені дуже допомогла!
LiuWenbin_NO.

Це єдина відповідь, яка справді зосереджує текст.
Джорі

64

Вирівняти вертикально складно, тому що сталося спуск тексту та підйом, багато хлопців використовували Paint.getTextBounds () для отримання TextWidth та TextHeight, але це не робить текст центру дуже сильним. Тут ми можемо використовувати Paint.measureText () для обчислення ширини тексту, TextHeight ми просто робимо віднімання зі спуском і підйомом, тоді ми отримали найбільш підхід TextSize, наступна робота досить легка один для одного.

// the Paint instance(should be assign as a field of class).
Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setTextSize(getResources().getDimension(R.dimen.btn_textsize));

// the display area.
Rect areaRect = new Rect(0, 0, 240, 60);

// draw the background style (pure color or image)
mPaint.setColor(Color.BLACK);
yourCanvas.drawRect(areaRect, mPaint);

String pageTitle = "文字小说";

RectF bounds = new RectF(areaRect);
// measure text width
bounds.right = mPaint.measureText(pageTitle, 0, pageTitle.length());
// measure text height
bounds.bottom = mPaint.descent() - mPaint.ascent();

bounds.left += (areaRect.width() - bounds.right) / 2.0f;
bounds.top += (areaRect.height() - bounds.bottom) / 2.0f;

mPaint.setColor(Color.WHITE);
yourCanvas.drawText(pageTitle, bounds.left, bounds.top - mPaint.ascent(), mPaint);

екран, знятий кодом

До речі, ми настійно рекомендуємо використовувати RectF , а не Rect , тому що позиції потребують в більш точних значень, з мого досвіду, RectF зробити верх & нижній відхилення тільки один піксель на xhdpi пристрої, Rect буде ще два.


Як написано, var "Paint" заплутаний. Як це призначається? як воно знає сходження та спуск тексту за межі.низу?
RoundSparrow hilltx

1
так, я оптимізував свою відповідь на це, будь ласка, погляньте.
VinceStyling

Дякую, що заощадили мій дорогоцінний час))
Андрій

16

Ваш код малює центр основної лінії тексту, в центрі подання. Для того, щоб центрувати текст у якийсь момент, x, y, потрібно обчислити центр тексту і поставити його в точку.

Цей метод намалює текст із центром у точці x, y. Якщо ви передасте його в центр свого перегляду, він намалює текст в центрі.

private void drawTextCentered(String text, int x, int y, Paint paint, Canvas canvas) {
    int xPos = x - (int)(paint.measureText(text)/2);
    int yPos = (int) (y - ((textPaint.descent() + textPaint.ascent()) / 2)) ;

    canvas.drawText(text, xPos, yPos, textPaint);
}

@MarcioGranzotto його історія android.graphics.Paintвикористовується для малювання тексту
Вільям Рід

4

Я вважаю, що найкращим рішенням для центрування тексту є таке:

textPaint.setTextAlign(Paint.Align.CENTER);
//textPaint is the Paint object being used to draw the text (it must be initialized beforehand)
float textY=center.y;
float textX=center.x; 
// in this case, center.x and center.y represent the coordinates of the center of the rectangle in which the text is being placed
canvas.drawText(text,textX,textY,textPaint);    `

Це виглядає саме так, як запитувач намагався центрувати текст. Текст не буде по центру вертикально, оскільки Paint.Align.Center не означає, що опорна точка тексту вертикально розташована.
andreas1724

3

працює для мене: textPaint.textAlign = Paint.Align.CENTER з textPaint.getTextBounds

private fun drawNumber(i: Int, canvas: Canvas, translate: Float) {
            val text = "$i"
            textPaint.textAlign = Paint.Align.CENTER
            textPaint.getTextBounds(text, 0, text.length, textBound)

            canvas.drawText(
                    "$i",
                    translate + circleRadius,
                    (height / 2 + textBound.height() / 2).toFloat(),
                    textPaint
            )
        }

Результат:

введіть тут опис зображення


1

Я створюю метод для спрощення цього:

    public static void drawCenterText(String text, RectF rectF, Canvas canvas, Paint paint) {
    Paint.Align align = paint.getTextAlign();
    float x;
    float y;
    //x
    if (align == Paint.Align.LEFT) {
        x = rectF.centerX() - paint.measureText(text) / 2;
    } else if (align == Paint.Align.CENTER) {
        x = rectF.centerX();
    } else {
        x = rectF.centerX() + paint.measureText(text) / 2;
    }
    //y
    metrics = paint.getFontMetrics();
    float acent = Math.abs(metrics.ascent);
    float descent = Math.abs(metrics.descent);
    y = rectF.centerY() + (acent - descent) / 2f;
    canvas.drawText(text, x, y, paint);

    Log.e("ghui", "top:" + metrics.top + ",ascent:" + metrics.ascent
            + ",dscent:" + metrics.descent + ",leading:" + metrics.leading + ",bottom" + metrics.bottom);
}

rectF - область, на якій потрібно намалювати текст, ось і все. Деталі


1

Якщо ми використовуємо Статичний макет

mStaticLayout = new StaticLayout(mText, mTextPaint, mTextWidth,
                Layout.Alignment.ALIGN_CENTER, 1.0f, 0, true);

Layout.Alignment.ALIGN_CENTER, це зробить трюк. Статична компонування також отримала масу інших переваг.

Довідка: Документація на Android


1

Це працювало для мене:

 paint.setTextAlign(Paint.Align.CENTER);
        int xPos = (newWidth / 2);
        int yPos = (newHeight / 2);
        canvas.drawText("Hello", xPos, yPos, paint);

якщо хтось знайде якусь проблему, будь ласка, повідомте мені про це


він почне писати текст із центральної точки, але весь текст не буде в центрі
М Шабан Алі

1

У моєму випадку мені не довелося ставити текст посередині полотна, а в колесо, яке крутиться. Хоча мені довелося використовувати цей код, щоб досягти успіху:

fun getTextRect(textSize: Float, textPaint: TextPaint, string: String) : PointF {
    val rect = RectF(left, top, right, bottom)
    val rectHeight = Rect()
    val cx = rect.centerX()
    val cy = rect.centerY()

    textPaint.getTextBounds(string, 0, string.length, rectHeight)
    val y = cy + rectHeight.height()/2
    val x = cx - textPaint.measureText(string)/2

    return PointF(x, y)
}

Потім я називаю цей метод із класу View:

private fun drawText(canvas: Canvas, paint: TextPaint, text: String, string: String) {
    val pointF = getTextRect(paint.textSize, textPaint, string)
    canvas.drawText(text, pointF!!.x, pointF.y, paint)
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.