Teks Android Center di atas kanvas

191

Saya mencoba menampilkan teks menggunakan kode di bawah ini. Masalahnya adalah bahwa teks tidak terpusat secara horizontal. Ketika saya mengatur koordinat untukdrawText , itu menetapkan bagian bawah teks pada posisi ini. Saya ingin teks yang akan digambar sehingga teks dipusatkan juga secara horizontal.

Ini adalah gambar untuk menampilkan masalah saya lebih lanjut:

Tangkapan layar

@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
sumber
20
Saya tidak akan mendeklarasikan objek cat Anda di dalam acara onDraw Anda. Itu akan diciptakan kembali setiap kali digambar ulang. Pertimbangkan menjadikannya variabel kelas privat.
Christopher Rathgeb
8
Pasangan! Harap sebutkan bahwa tautan gambar Anda adalah NSFW! Saya tidak bermaksud bersikap sopan, tetapi saya tidak perlu iklan dengan wanita topless muncul di layar saya di kantor.
Michael Scheper
5
@MichaelScheper Maaf, saya telah memperbarui tautannya!
Sebastian
23
@Sebastian Hei kamu masih punya tautan itu?
S.Lukas
@ S.Lukas hhahaha
BOTJr.

Jawaban:

375

Coba yang berikut ini:

 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
sumber
10
Jawaban yang bagus Bagi saya, saya menggunakan yang berikut karena saya perlu memusatkan teks secara horizontal sepenuhnya daripada teks untuk memulai di posisi tengah: int xPos = (Lebar - textPaint.TextSize * Math.Abs ​​(_text.Length / 2)) / 2; Tidak yakin apakah ada cara yang lebih baik untuk mencapai ini.
paj7777
Dan mungkin casting _text terbaik. Panjang untuk float karena jelas tidak akan bekerja untuk panjang teks aneh.
paj7777
61
paj7777, itu tidak perlu jika Anda mengatur textPaint.setTextAlign (Align.CENTER);
Costi Muraru
@ paj7777 Itu hanya akan berfungsi untuk font dengan lebar tetap. Juga, Anda tidak perlu cor untuk pelampung; jika Anda membaginya dengan float, hasilnya akan menjadi float. mis. float halfLength = text.length() / 2f;Ini disebut tipe promosi .
Michael Scheper
2
Saya membandingkan pendekatan ini (= pusat dengan Paint.descent()dan Paint.ascent()) dengan pendekatan ke pusat teks dengan Paint.getTextBounds()jawaban saya di bawah. Paint.descent()dan Paint.ascent()jangan memperhitungkan teks yang sebenarnya. (Anda dapat mengenali ketidakakuratan ini dalam tangkapan layar di pos saya di bawah ini.) Itulah mengapa saya akan merekomendasikan menentang pendekatan ini. Pendekatan dengan Paint.getTextBounds()tampaknya bekerja lebih akurat.
andreas1724
215

Pusatkan dengan Paint.getTextBounds () :

masukkan deskripsi gambar di sini

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 tidak berarti bahwa titik referensi teks terpusat secara vertikal. Titik referensi selalu ada di garis dasar. Jadi, mengapa tidak menggunakan Paint.Align.LEFT ? Anda harus tetap menghitung titik referensi.

  • Paint.descent () memiliki kekurangan, yaitu tidak mempertimbangkan teks asli. Paint.descent () mengambil nilai yang sama, terlepas dari apakah teks berisi huruf dengan keturunan atau tidak. Itu sebabnya saya menggunakan r.bottom sebagai gantinya.

  • Saya punya beberapa masalah dengan Canvas.getHeight () jika API <16. Karena itulah saya menggunakan Canvas.getClipBounds (Rect) . (Jangan gunakan Canvas.getClipBounds (). GetHeight () karena mengalokasikan memori untuk Rect .)

  • Untuk alasan kinerja, Anda harus mengalokasikan objek sebelum digunakan di onDraw () . Sebagai drawCenter () akan dipanggil di dalam onDraw () objek Rect r dialokasikan sebagai bidang di sini.


Saya mencoba memasukkan kode dua jawaban teratas ke dalam kode saya sendiri (Agustus 2015) dan membuat tangkapan layar untuk membandingkan hasilnya:

teks berpusat tiga versi

Teks harus dipusatkan di dalam persegi panjang penuh merah. Kode saya menghasilkan teks putih, dua kode lainnya sama sekali menghasilkan teks abu-abu (sebenarnya sama, tumpang tindih). Teks abu-abu sedikit terlalu rendah dan dua di sebelah kanan.

Inilah cara saya melakukan tes:

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
sumber
1
Terima kasih banyak .. Logika Anda sangat membantu saya.
Sujay UN
Terima kasih banyak .. Logika Anda juga sangat membantu saya!
LiuWenbin_NO.
Ini adalah satu-satunya jawaban yang benar-benar memusatkan teks.
Joery
63

Menyelaraskan secara vertikal sulit karena penurunan teks dan kenaikan, banyak orang menggunakan Paint.getTextBounds () untuk mengambil TextWidth dan TextHeight, tetapi itu tidak membuat pusat teks terlalu banyak. Di sini kita dapat menggunakan Paint.measureText () untuk menghitung TextWidth, TextHeight yang kita lakukan dengan mengurangkan dengan menurun dan naik, maka kita mendapat pendekatan TextSize paling banyak, pekerjaan berikut ini cukup mudah untuk satu sama lain.

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

tangkapan layar oleh kode

Omong-omong, kami sangat merekomendasikan menggunakan RectF daripada Rect karena posisi membutuhkan nilai yang lebih akurat, dalam pengalaman saya, RectF melakukan penyimpangan atas & bawah hanya satu piksel pada perangkat xhdpi, Rect akan menjadi dua lagi.

VinceStyling
sumber
Seperti ditulis, var "Paint" membingungkan. Bagaimana cara menetapkannya? bagaimana cara mengetahui naik dan turunnya teks untuk bounds.bottom?
RoundSparrow hilltx
1
ya, saya mengoptimalkan jawaban saya untuk ini, silakan lihat.
VinceStyling
Terima kasih telah menghemat waktu saya yang berharga))
Andrey
16

Kode Anda menggambar pusat garis dasar teks, di tengah tampilan. Dalam rangka untuk pusat teks di beberapa titik, x, y, Anda perlu menghitung pusat teks, dan menempatkan bahwa pada titik.

Metode ini akan menggambar teks yang terpusat di titik x, y. Jika Anda melewatinya tengah tampilan Anda, itu akan menarik teks yang terpusat.

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
sumber
@MarcioGranzotto ini android.graphics.Paintsedang digunakan untuk menggambar teks
William Reed
4

Saya menemukan bahwa solusi terbaik untuk teks keterpusatan adalah sebagai berikut:

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
sumber
Ini persis seperti caranya, si penanya mencoba memusatkan teks. Teks tidak akan dipusatkan secara vertikal karena Paint.Align.Center tidak berarti, bahwa titik referensi teks terpusat secara vertikal.
andreas1724
3

berfungsi untuk saya gunakan: textPaint.textAlign = Paint.Align.CENTER dengan 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
            )
        }

hasilnya adalah:

masukkan deskripsi gambar di sini

Serg Burlaka
sumber
1

Saya membuat metode untuk menyederhanakan ini:

    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 adalah area yang Anda inginkan untuk menggambar teks, Itu saja. Detail

Seth
sumber
1

Jika kita menggunakan tata letak statis

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

Layout.Alignment.ALIGN_CENTER ini akan melakukan trik. Tata letak statis juga telah mendapat banyak keunggulan lainnya.

Referensi: Dokumentasi Android

hushed_voice
sumber
1

Ini bekerja untuk saya:

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

jika ada yang menemukan masalah tolong ket saya tahu

JSONParser
sumber
itu akan mulai menulis teks dari titik tengah tetapi keseluruhan teks tidak akan berada di tengah
M Shaban Ali
0

Dalam kasus saya, saya tidak harus meletakkan teks di tengah kanvas, tetapi di roda yang berputar. Meskipun saya harus menggunakan kode ini untuk berhasil:

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

Lalu saya memanggil metode ini dari kelas 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
sumber