Texte du centre Android sur le canevas

191

J'essaye d'afficher un texte en utilisant le code ci-dessous. Le problème est que le texte n'est pas centré horizontalement. Lorsque je règle les coordonnées pour drawText, il définit le bas du texte à cette position. Je voudrais que le texte soit dessiné de sorte que le texte soit centré également horizontalement.

Voici une image pour afficher davantage mon problème:

Capture d'écran

@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);
}
Sébastien
la source
20
Je ne déclarerais pas votre objet de peinture dans votre événement onDraw. Il est recréé à chaque fois qu'il est redessiné. Pensez à en faire une variable de classe privée.
Christopher Rathgeb
8
Camarade! Veuillez mentionner que votre lien d'image est NSFW! Je ne veux pas être prude, mais je n'ai pas besoin de publicités avec des femmes aux seins nus apparaissant sur mon écran au bureau.
Michael Scheper
5
@MichaelScheper Désolé, j'ai mis à jour le lien!
Sebastian
23
@Sebastian Hé, tu as toujours ce lien?
S.Lukas
@ S.Lukas hhahaha
BOTJr.

Réponses:

378

Essayez ce qui suit:

 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);
Arun George
la source
10
Très bonne réponse. Pour moi, j'ai utilisé ce qui suit car j'avais besoin de centrer complètement le texte horizontalement plutôt que le texte pour commencer à la position centrale: int xPos = (Width - textPaint.TextSize * Math.Abs ​​(_text.Length / 2)) / 2; Je ne sais pas s'il existe une meilleure façon d'accomplir cela.
paj7777
Et il est probablement préférable de convertir _text.Length en flottant car cela ne fonctionnera évidemment pas pour des longueurs de texte impaires.
paj7777
61
paj7777, ce n'est pas nécessaire si vous définissez textPaint.setTextAlign (Align.CENTER);
Costi Muraru
@ paj7777 Cela n'aurait fonctionné que pour les polices à largeur fixe de toute façon. De plus, vous n'avez pas besoin de lancer un flotteur; si vous divisez par un flottant, le résultat sera un flottant. par exemple, float halfLength = text.length() / 2f;cela s'appelle la promotion de type .
Michael Scheper
2
J'ai comparé cette approche (= centrer avec Paint.descent()et Paint.ascent()) avec l'approche pour centrer le texte Paint.getTextBounds()dans ma réponse ci-dessous. Paint.descent()et Paint.ascent()ne prennent pas en compte le texte actuel. (Vous pouvez reconnaître cette inexactitude dans la capture d'écran dans mon message ci-dessous.) C'est pourquoi je recommanderais de ne pas utiliser cette approche. L'approche avec Paint.getTextBounds()semble fonctionner plus précise.
andreas1724
218

Centrer avec Paint.getTextBounds () :

entrez la description de l'image ici

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 ne signifie pas que le point de référence du texte est centré verticalement. Le point de référence est toujours sur la ligne de base. Alors, pourquoi ne pas utiliser Paint.Align.LEFT ? Vous devez quand même calculer le point de référence.

  • Paint.descent () a l'inconvénient de ne pas considérer le vrai texte. Paint.descent () récupère la même valeur, que le texte contienne ou non des lettres descendantes. C'est pourquoi j'utilise r.bottom à la place.

  • J'ai eu quelques problèmes avec Canvas.getHeight () si API <16. C'est pourquoi j'utilise plutôt Canvas.getClipBounds (Rect) . (N'utilisez pas Canvas.getClipBounds (). GetHeight () car il alloue de la mémoire pour un Rect .)

  • Pour des raisons de performances, vous devez allouer les objets avant qu'ils ne soient utilisés dans onDraw () . Comme drawCenter () sera appelé dans onDraw (), l'objet Rect r est préalloué comme champ ici.


J'ai essayé de mettre le code des deux principales réponses dans mon propre code (août 2015) et j'ai fait une capture d'écran pour comparer les résultats:

texte centré trois versions

Le texte doit être centré dans le rectangle rempli de rouge. Mon code produit le texte blanc, les deux autres codes produisent tout à fait le texte gris (ils sont en fait les mêmes, se chevauchant). Le texte gris est un peu trop bas et deux beaucoup à droite.

Voici comment j'ai fait le test:

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);
    }
}
andreas1724
la source
1
Merci beaucoup. Votre logique m'a vraiment beaucoup aidé.
Sujay UN
Merci beaucoup. Votre logique m'a beaucoup aidé aussi!
LiuWenbin_NO.
C'est la seule réponse qui centre vraiment le texte.
Joery le
64

Aligner verticalement est difficile car la descente et la remontée du texte se sont produites, beaucoup de gars ont utilisé Paint.getTextBounds () pour récupérer TextWidth et TextHeight, mais cela ne fait pas beaucoup le centre du texte. Ici, nous pouvons utiliser Paint.measureText () pour calculer le TextWidth, le TextHeight que nous faisons simplement en soustrayant avec la descente et l'ascension, puis nous avons le plus d'approche TextSize, le travail suivant est assez facile l'un pour l'autre.

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

capture d'écran par le code

À propos, nous recommandons fortement d'utiliser RectF plutôt que Rect car les positions nécessitent des valeurs plus précises.Dans mon expérience, RectF a fait la déviation haut et bas d'un seul pixel sur l'appareil xhdpi, Rect serait deux de plus.

VinceStyling
la source
Comme écrit, le var "Paint" est déroutant. Comment est-il attribué? comment connaît-il l'ascension et la descente du texte pour les borniers du bas?
RoundSparrow hilltx
1
oui, j'ai optimisé ma réponse pour cela, veuillez jeter un œil.
VinceStyling
Merci d'avoir économisé mon temps précieux))
Andrey
16

Votre code dessine le centre de la ligne de base du texte, au centre de la vue. Pour centrer le texte à un moment donné, x, y, vous devez calculer le centre du texte, et de mettre ce au point.

Cette méthode dessinera le texte centré au point x, y. Si vous lui passez le centre de votre vue, il dessinera le texte centré.

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);
}
Borislav Markov
la source
@MarcioGranzotto c'est android.graphics.Paintêtre utilisé pour dessiner le texte
William Reed
4

Je trouve que la meilleure solution pour centrer le texte est la suivante:

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);    `
Daniel
la source
Cela ressemble exactement à la façon dont l'interrogateur a essayé de centrer le texte. Le texte ne sera pas centré verticalement car Paint.Align.Center ne signifie pas que le point de référence du texte est centré verticalement.
andreas1724
3

fonctionne pour moi d'utiliser: textPaint.textAlign = Paint.Align.CENTER avec 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
            )
        }

le résultat est:

entrez la description de l'image ici

Serg Burlaka
la source
1

Je crée une méthode pour simplifier cela:

    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 est la zone dans laquelle vous voulez dessiner le texte, c'est tout. Détails

Seth
la source
1

Si nous utilisons une disposition statique

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

Layout.Alignment.ALIGN_CENTER cela fera l'affaire. La disposition statique présente également de nombreux autres avantages.

Référence: Documentation Android

hushed_voice
la source
1

Cela a fonctionné pour moi:

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

si quelqu'un trouve un problème, merci de me le faire savoir

JSONParser
la source
il commencera à écrire le texte à partir du point central mais le texte entier ne sera pas au centre
M Shaban Ali
1

Dans mon cas, je n'ai pas eu à mettre le texte au milieu d'une toile, mais dans une roue qui tourne. Bien que j'ai dû utiliser ce code pour réussir:

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

Ensuite, j'appelle cette méthode à partir de la classe 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)
}
Mattia Ferigutti
la source