Android Center Text auf Leinwand


191

Ich versuche, einen Text mit dem folgenden Code anzuzeigen. Das Problem ist, dass der Text nicht horizontal zentriert ist. Wenn ich die Koordinaten für drawTexteinstelle, wird der untere Rand des Textes an dieser Position festgelegt. Ich möchte, dass der Text so gezeichnet wird, dass der Text auch horizontal zentriert ist.

Dies ist ein Bild, um mein Problem weiter anzuzeigen:

Bildschirmfoto

@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
Ich würde Ihr Malobjekt nicht in Ihrem onDraw-Ereignis deklarieren. Es wird jedes Mal neu erstellt, wenn es neu gezeichnet wird. Erwägen Sie, es zu einer privaten Klassenvariablen zu machen.
Christopher Rathgeb

8
Kamerad! Bitte erwähnen Sie, dass Ihr Bildlink NSFW ist! Ich will nicht prüde sein, aber ich brauche keine Werbung mit oben ohne Frauen, die auf meinem Bildschirm im Büro erscheinen.
Michael Scheper

5
@ MichaelScheper Sorry, ich habe den Link aktualisiert!
Sebastian

23
@ Sebastian Hey, hast du noch diesen Link?
Lukas

@ S.Lukas hhahaha
BOTJr.

Antworten:


375

Versuche Folgendes:

 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
Gute Antwort. Für mich habe ich Folgendes verwendet, um den Text horizontal vollständig zu zentrieren, anstatt ihn an der Mittelposition zu beginnen: int xPos = (Width - textPaint.TextSize * Math.Abs ​​(_text.Length / 2)) / 2; Ich bin mir nicht sicher, ob es einen besseren Weg gibt, dies zu erreichen.
paj7777

Und wahrscheinlich am besten _text.Length in einen Float umwandeln, da dies bei ungeraden Textlängen offensichtlich nicht funktioniert.
paj7777

61
paj7777, das ist nicht erforderlich, wenn Sie textPaint.setTextAlign (Align.CENTER) festlegen;
Costi Muraru

@ paj7777 Das hätte sowieso nur für Schriften mit fester Breite funktioniert. Außerdem müssen Sie nicht auf einen Schwimmer werfen . Wenn Sie durch einen Float teilen, ist das Ergebnis ein Float. zB float halfLength = text.length() / 2f;Dies wird als Typwerbung bezeichnet .
Michael Scheper

2
Ich habe diesen Ansatz (= zentrieren mit Paint.descent()und Paint.ascent()) mit dem Ansatz zum Zentrieren von Text Paint.getTextBounds()in meiner Antwort unten verglichen . Paint.descent()und Paint.ascent()berücksichtigen Sie nicht den tatsächlichen Text. (Sie können diese Ungenauigkeit im Screenshot in meinem Beitrag unten erkennen.) Deshalb würde ich gegen diesen Ansatz empfehlen. Der Ansatz mit Paint.getTextBounds()scheint genauer zu funktionieren.
andreas1724

214

Mit Paint.getTextBounds () zentrieren :

Geben Sie hier die Bildbeschreibung ein

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 bedeutet nicht, dass der Referenzpunkt des Textes vertikal zentriert ist. Der Referenzpunkt liegt immer auf der Grundlinie. Warum also nicht Paint.Align.LEFT verwenden ? Sie müssen den Referenzpunkt trotzdem berechnen.

  • Paint.descent () hat den Nachteil, dass der echte Text nicht berücksichtigt wird. Paint.descent () ruft denselben Wert ab, unabhängig davon, ob der Text Buchstaben mit Abfahrten enthält oder nicht. Deshalb benutze ich stattdessen r.bottom .

  • Ich hatte einige Probleme mit Canvas.getHeight (), wenn API <16. Deshalb verwende ich stattdessen Canvas.getClipBounds (Rect) . (Nicht verwenden Canvas.getClipBounds (). GetHeight () , wie es Speicher für einen zuordnet Rect .)

  • Aus Leistungsgründen sollten Sie Objekte zuweisen, bevor sie in onDraw () verwendet werden . Wie drawCenter () wird innerhalb von aufgerufen werden OnDraw () das Objekt Rect r wird als Feld hier vorbelegt.


Ich habe versucht, den Code der beiden Top-Antworten in meinen eigenen Code einzufügen (August 2015) und einen Screenshot erstellt, um die Ergebnisse zu vergleichen:

Text zentriert drei Versionen

Der Text sollte innerhalb des rot gefüllten Rechtecks ​​zentriert sein. Mein Code erzeugt den weißen Text, die anderen beiden Codes erzeugen insgesamt den grauen Text (sie sind tatsächlich gleich und überlappen sich). Der graue Text ist etwas zu niedrig und zwei viel rechts.

So habe ich den Test gemacht:

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
Vielen Dank. Ihre Logik hat mir sehr geholfen.
Sujay UN

Vielen Dank. Ihre Logik hat mir auch sehr geholfen!
LiuWenbin_NO.

63

Das vertikale Ausrichten ist schwierig, da Textabstieg und -aufstieg stattgefunden haben. Viele Leute haben Paint.getTextBounds () verwendet , um TextWidth und TextHeight abzurufen, aber das Textzentrum wird dadurch nicht sehr stark. Hier können wir Paint.measureText () verwenden , um die Textbreite zu berechnen, die Texthöhe, die wir einfach mit Abstieg und Aufstieg subtrahieren. Dann haben wir den besten Ansatz für TextSize. Die folgende Arbeit ist ziemlich einfach für einander.

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

Screenshot vom Code

By the way, wir hoch Verwendung empfehlen RectF anstatt Rect , weil die Positionen genauere Werte müssen, in meiner Erfahrung, RectF die obere und untere Abweichung nur ein Pixel auf xhdpi Gerät durchgeführt, würde Rect zwei mehr sein.


Wie geschrieben, ist die Var "Paint" verwirrend. Wie ist es zugeordnet? Woher kennt es den Auf- und Abstieg des Textes für bounds.bottom?
RoundSparrow Hilltx

1
Ja, ich habe meine Antwort darauf optimiert. Bitte werfen Sie einen Blick darauf.
VinceStyling

Danke, dass du meine kostbare Zeit gespart hast))
Andrey

16

Ihr Code zeichnet die Mitte der Grundlinie des Textes in der Mitte der Ansicht. Um den Text zu einem bestimmten Zeitpunkt, x, y zum Zentrum, müssen Sie die Mitte des Textes berechnen und setzen , dass an der Spitze.

Diese Methode zeichnet Text, der am Punkt x, y zentriert ist. Wenn Sie es in der Mitte Ihrer Ansicht übergeben, wird der Text zentriert gezeichnet.

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 Es android.graphics.Paintwird verwendet, um den Text zu zeichnen
William Reed

4

Ich finde, dass die beste Lösung zum Zentrieren von Text wie folgt ist:

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

Das sieht genauso aus wie der Fragesteller, der versucht hat, den Text zu zentrieren. Der Text wird nicht vertikal zentriert, da Paint.Align.Center nicht bedeutet, dass der Referenzpunkt des Textes vertikal zentriert ist.
andreas1724

3

funktioniert für mich zu verwenden: textPaint.textAlign = Paint.Align.CENTER mit 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
            )
        }

Ergebnis ist:

Geben Sie hier die Bildbeschreibung ein


1

Ich erstelle eine Methode, um dies zu vereinfachen:

    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 ist der Bereich, in dem Sie den Text zeichnen möchten. Das war's. Einzelheiten


1

Wenn wir statisches Layout verwenden

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

Layout.Alignment.ALIGN_CENTER Dies reicht aus. Das statische Layout hat noch viele andere Vorteile.

Referenz: Android-Dokumentation


1

Das hat bei mir funktioniert:

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

Wenn jemand ein Problem findet, lass es mich wissen


Es wird Text vom Mittelpunkt aus schreiben, aber der gesamte Text wird nicht in der Mitte sein
M Shaban Ali

0

In meinem Fall musste ich den Text nicht in die Mitte einer Leinwand legen, sondern in ein Rad, das sich dreht. Obwohl ich diesen Code verwenden musste, um erfolgreich zu sein:

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

Dann rufe ich diese Methode aus der View-Klasse auf:

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)
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.