Texto de Android Center en lienzo

191

Estoy tratando de mostrar un texto usando el código a continuación. El problema es que el texto no está centrado horizontalmente. Cuando configuro las coordenadas para drawText, establece la parte inferior del texto en esta posición. Me gustaría que el texto se dibuje para que el texto esté centrado también horizontalmente.

Esta es una imagen para mostrar más mi problema:

Captura de pantalla

@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);
}
Sebastian
fuente
20
No declararía su objeto de pintura dentro de su evento onDraw. Se recrea cada vez que se vuelve a dibujar. Considere convertirlo en una variable de clase privada.
Christopher Rathgeb
8
¡Compañero! ¡Por favor mencione que su enlace de imagen es NSFW! No pretendo ser mojigato, pero no necesito anuncios con mujeres en topless que aparecen en mi pantalla en la oficina.
Michael Scheper
55
@MichaelScheper Lo siento, ¡he actualizado el enlace!
Sebastian
23
@Sebastian Hola, ¿todavía tienes ese enlace?
S.Lukas
@ S.Lukas jajaja
BOTJr.

Respuestas:

375

Intenta lo siguiente:

 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
fuente
10
Gran respuesta. Para mí, utilicé lo siguiente porque necesitaba centrar el texto horizontalmente por completo en lugar del texto para comenzar en la posición central: int xPos = (Width - textPaint.TextSize * Math.Abs ​​(_text.Length / 2)) / 2; No estoy seguro si hay una mejor manera de lograr esto.
paj7777
Y probablemente sea mejor enviar _text.Length a un flotante, ya que obviamente no funcionará para longitudes de texto extrañas.
paj7777
61
paj7777, eso no es necesario si configura textPaint.setTextAlign (Align.CENTER);
Costi Muraru
@ paj7777 De todos modos, eso solo hubiera funcionado para fuentes de ancho fijo. Además, no necesita lanzar a un flotador; Si divide por un flotador, el resultado será un flotador. Por ejemplo, float halfLength = text.length() / 2f;esto se llama promoción de tipo .
Michael Scheper
2
Comparé este enfoque (= centrar con Paint.descent()y Paint.ascent()) con el enfoque de centrar texto Paint.getTextBounds()en mi respuesta a continuación. Paint.descent()y Paint.ascent()no tenga en cuenta el texto real. (Puede reconocer esta inexactitud en la captura de pantalla de mi publicación a continuación). Es por eso que recomendaría este enfoque. El enfoque con Paint.getTextBounds()parece funcionar más preciso.
andreas1724
215

Centro con Paint.getTextBounds () :

ingrese la descripción de la imagen aquí

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 no significa que el punto de referencia del texto esté centrado verticalmente. El punto de referencia siempre está en la línea de base. Entonces, ¿por qué no usar Paint.Align.LEFT ? Tienes que calcular el punto de referencia de todos modos.

  • Paint.descent () tiene la desventaja de que no considera el texto real. Paint.descent () recupera el mismo valor, independientemente de si el texto contiene letras con descensos o no. Es por eso que uso r.bottom en su lugar.

  • He tenido algunos problemas con Canvas.getHeight () si API <16. Es por eso que uso Canvas.getClipBounds (Rect) en su lugar. (No use Canvas.getClipBounds (). GetHeight () ya que asigna memoria para un Rect .)

  • Por razones de rendimiento, debe asignar objetos antes de que se usen en onDraw () . Como drawCenter () será llamada dentro onDraw () del objeto Rect r está preasignado como un campo aquí.


Traté de poner el código de las dos respuestas principales en mi propio código (agosto de 2015) e hice una captura de pantalla para comparar los resultados:

texto centrado en tres versiones

El texto debe estar centrado dentro del rectángulo relleno de rojo. Mi código produce el texto blanco, los otros dos códigos producen en conjunto el texto gris (en realidad son lo mismo, se superponen). El texto gris es un poco demasiado bajo y dos más a la derecha.

Así es como hice la prueba:

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
fuente
1
Muchas gracias. Tu lógica realmente me ayudó mucho.
Sujay ONU
Muchas gracias ... ¡Tu lógica también me ayudó mucho!
LiuWenbin_NO.
Esta es la única respuesta que realmente centra el texto.
Joery hace
63

Alinear verticalmente es difícil porque sucedió el descenso y el ascenso del texto, muchos chicos usaron Paint.getTextBounds () para recuperar TextWidth y TextHeight, pero no hace que el texto se centre demasiado. Aquí podemos usar Paint.measureText () para calcular TextWidth, TextHeight que simplemente restamos con descenso y ascenso, luego obtuvimos la mayor aproximación TextSize, el siguiente trabajo es bastante fácil el uno para el otro.

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

captura de pantalla por el código

Por cierto, recomendamos usar RectF en lugar de Rect porque las posiciones necesitan valores más precisos, en mi experiencia, RectF realizó la desviación superior e inferior solo un píxel en el dispositivo xhdpi, Rect sería dos más.

VinceStyling
fuente
Como está escrito, la var "Paint" es confusa. ¿Cómo se asigna? ¿Cómo sabe el ascenso y el descenso del texto para los límites.
RoundSparrow hilltx
1
Sí, optimicé mi respuesta para esto, por favor, eche un vistazo.
VinceStyling
Gracias por ahorrar mi precioso tiempo))
Andrey
16

Su código está dibujando el centro de la línea de base del texto, en el centro de la vista. Para centrar el texto en algún punto, x, y, debe calcular el centro del texto y poner eso en el punto.

Este método dibujará texto centrado en el punto x, y. Si lo pasa al centro de su vista, dibujará el texto centrado.

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
fuente
@MarcioGranzotto es el que android.graphics.Paintse usa para dibujar el texto
William Reed
4

Encuentro que la mejor solución para centrar texto es la siguiente:

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
fuente
Esto se parece exactamente a la forma en que el interrogador trató de centrar el texto. El texto no estará centrado verticalmente porque Paint.Align.Center no significa que el punto de referencia del texto esté centrado verticalmente.
andreas1724
3

funciona para mí usar: textPaint.textAlign = Paint.Align.CENTER con 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
            )
        }

el resultado es:

ingrese la descripción de la imagen aquí

Serg Burlaka
fuente
1

Creo un método para simplificar esto:

    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 es el área que desea dibujar el texto, eso es todo. Detalles

Seth
fuente
1

Si estamos usando diseño estático

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

Layout.Alignment.ALIGN_CENTER esto hará el truco. El diseño estático también tiene muchas otras ventajas.

Referencia: Documentación de Android

voz silenciada
fuente
1

Esto funcionó para mí:

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

si alguien encuentra algún problema por favor avíseme

JSONParser
fuente
comenzará a escribir texto desde el punto central, pero el texto completo no estará en el centro
M Shaban Ali
0

En mi caso, no tuve que poner el texto en el medio de un lienzo, sino en una rueda que gira. Aunque tuve que usar este código para tener éxito:

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

Luego llamo a este método desde la clase 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
fuente