Văn bản Android Center trên canvas


191

Tôi đang cố gắng hiển thị một văn bản bằng cách sử dụng mã dưới đây. Vấn đề là văn bản không được căn giữa theo chiều ngang. Khi tôi đặt tọa độ cho drawText, nó sẽ đặt dưới cùng của văn bản ở vị trí này. Tôi muốn văn bản được vẽ để văn bản được căn giữa cũng theo chiều ngang.

Đây là một hình ảnh để hiển thị thêm vấn đề của tôi:

Ảnh chụp màn hình

@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
Tôi sẽ không khai báo đối tượng vẽ của bạn bên trong sự kiện onDraw của bạn. Nó được tái tạo mỗi khi nó được vẽ lại. Xem xét làm cho nó một biến lớp riêng.
Christopher Rath Quay

8
Người bạn đời! Hãy đề cập rằng liên kết hình ảnh của bạn là NSFW! Tôi không có ý thận trọng, nhưng tôi không cần quảng cáo với phụ nữ ngực trần xuất hiện trên màn hình của tôi trong văn phòng.
Michael Scheper

5
@MichaelScheper Xin lỗi, tôi đã cập nhật liên kết!
Sebastian

23
@Sebastian Này bạn vẫn có liên kết đó chứ?
S.Lukas

@ S.Lukas hhahaha
BOTJr.

Câu trả lời:


375

Hãy thử như sau:

 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
Câu trả lời chính xác. Đối với tôi, tôi đã sử dụng những điều sau đây khi tôi cần căn giữa văn bản theo chiều ngang thay vì văn bản để bắt đầu ở vị trí trung tâm: int xPos = (Width - textPaint.TextSize * Math.Abs ​​(_text.Lạng / 2)) / 2; Không chắc chắn nếu có một cách tốt hơn để thực hiện điều này.
py7777

Và có lẽ tốt nhất là truyền _text. Bước tới độ nổi vì rõ ràng nó sẽ không hoạt động đối với độ dài văn bản lẻ.
py7777

61
pyj7777, điều đó không cần thiết nếu bạn đặt textPaint.setTextAlign (Align.CENTER);
Costi Muraru 16/12/13

@ py7777 Dù sao thì nó cũng chỉ có tác dụng với phông chữ có chiều rộng cố định. Ngoài ra, bạn không cần phải cast vào một phao; nếu bạn chia cho một float, kết quả sẽ là một float. vd: float halfLength = text.length() / 2f;Đây được gọi là khuyến mãi kiểu .
Michael Scheper

2
Tôi đã so sánh cách tiếp cận này (= giữa với Paint.descent()Paint.ascent()) với cách tiếp cận văn bản trung tâm với Paint.getTextBounds()câu trả lời của tôi dưới đây. Paint.descent()Paint.ascent()không tính đến văn bản thực tế. (Bạn có thể nhận ra sự không chính xác này trong ảnh chụp màn hình trong bài viết của tôi dưới đây.) Đó là lý do tại sao tôi khuyên bạn nên chống lại phương pháp này. Cách tiếp cận với Paint.getTextBounds()dường như làm việc chính xác hơn.
andreas1724

214

Trung tâm với Paint.getTextBound () :

nhập mô tả hình ảnh ở đây

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 không có nghĩa là điểm tham chiếu của văn bản được căn giữa theo chiều dọc. Điểm tham chiếu luôn nằm trên đường cơ sở. Vậy, tại sao không sử dụng Paint.Align.LEFT ? Bạn phải tính điểm tham chiếu nào.

  • Paint.descent () có nhược điểm là nó không xem xét văn bản thực. Paint.descent () lấy cùng một giá trị, bất kể văn bản có chứa các chữ cái có gốc hay không. Đó là lý do tại sao tôi sử dụng r.bottom thay thế.

  • Tôi đã gặp một số vấn đề với Canvas.getHeight () nếu API <16. Đó là lý do tại sao tôi sử dụng Canvas.getClipBound (Rect) thay thế. (Không sử dụng Canvas.getClipBound (). GetHeight () vì nó phân bổ bộ nhớ cho Rect .)

  • Vì lý do hiệu năng, bạn nên phân bổ các đối tượng trước khi chúng được sử dụng trong onDraw () . Vì drawCenter () sẽ được gọi trong onDraw () , đối tượng Rect r được đặt trước như một trường ở đây.


Tôi đã cố gắng đặt mã của hai câu trả lời hàng đầu vào mã của riêng tôi (tháng 8 năm 2015) và tạo một ảnh chụp màn hình để so sánh kết quả:

văn bản tập trung ba phiên bản

Văn bản nên được tập trung trong hình chữ nhật đầy màu đỏ. Mã của tôi tạo ra văn bản màu trắng, hai mã còn lại tạo ra văn bản màu xám hoàn toàn (chúng thực sự giống nhau, chồng chéo). Các văn bản màu xám là một chút quá thấp và hai bên phải.

Đây là cách tôi thực hiện bài kiểm tra:

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
Cảm ơn bạn rất nhiều .. Logic của bạn thực sự đã giúp tôi rất nhiều.
Sujay LHQ

Cảm ơn bạn rất nhiều .. Logic của bạn thực sự cũng giúp tôi rất nhiều!
LiuWenbin_NO.

63

Căn chỉnh theo chiều dọc là khó khăn vì việc hạ xuống và đi lên văn bản đã xảy ra, rất nhiều kẻ đã sử dụng Paint.getTextBound () để truy xuất TextWidth và TextHeight, nhưng nó không làm cho trung tâm văn bản rất nhiều. Ở đây chúng ta có thể sử dụng Paint.measureText () để tính toán TextWidth, TextHeight chúng ta chỉ cần trừ đi với gốc và đi lên, sau đó chúng ta có cách tiếp cận TextSize nhiều nhất, công việc sau đây khá dễ dàng cho nhau.

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

chụp màn hình theo mã

Nhân tiện, chúng tôi khuyên bạn nên sử dụng RectF thay vì Rect vì các vị trí cần giá trị chính xác hơn, theo kinh nghiệm của tôi, RectF đã thực hiện độ lệch trên & dưới chỉ một pixel trên thiết bị xhdpi, Rect sẽ là hai pixel nữa.


Như đã viết, var "Paint" gây nhầm lẫn. Nó được phân công như thế nào? Làm thế nào để nó biết sự đi lên và đi xuống của văn bản cho giới hạn.bottom?
RoundSparrow hilltx

1
vâng, tôi đã tối ưu hóa câu trả lời của mình cho việc này, xin hãy xem.
VinceStyling

Cảm ơn vì đã tiết kiệm thời gian quý báu của tôi))
Andrey

16

Mã của bạn đang vẽ trung tâm của đường cơ sở của văn bản, ở giữa của khung nhìn. Để căn giữa văn bản tại một số điểm, x, y, bạn cần tính toán trung tâm của văn bản và đặt vào điểm đó.

Phương pháp này sẽ vẽ văn bản tập trung tại điểm x, y. Nếu bạn vượt qua nó ở trung tâm của chế độ xem, nó sẽ vẽ văn bản ở giữa.

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 nó android.graphics.Paintđang được sử dụng để vẽ văn bản
William Reed

4

Tôi thấy rằng giải pháp tốt nhất cho việc định tâm văn bản như sau:

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

Điều này trông giống hệt như cách, người hỏi đã cố gắng tập trung vào văn bản. Văn bản sẽ không được căn giữa theo chiều dọc vì Paint.Align.Center không có nghĩa là điểm tham chiếu của văn bản được căn giữa theo chiều dọc.
andreas1724

3

hoạt động với tôi để sử dụng: textPaint.textAlign = Paint.Align.CENTER với textPaint.getTextBound

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

kết quả là:

nhập mô tả hình ảnh ở đây


1

Tôi tạo một phương thức để đơn giản hóa việc này:

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

orthF là khu vực bạn muốn vẽ văn bản, đó là nó. Chi tiết


1

Nếu chúng ta đang sử dụng Bố cục tĩnh

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

Giao diện.Alocation.ALIGN_CENTER điều này sẽ thực hiện thủ thuật. Bố cục tĩnh cũng có rất nhiều lợi thế khác.

Tham khảo: Tài liệu Android


1

Điều này làm việc cho tôi:

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

Nếu ai tìm thấy bất kỳ vấn đề xin vui lòng cho tôi biết


nó sẽ bắt đầu viết văn bản từ điểm trung tâm nhưng toàn bộ văn bản sẽ không ở giữa
M Shaban Ali

0

Trong trường hợp của tôi, tôi đã không phải đặt văn bản ở giữa một khung vẽ, nhưng trong một bánh xe quay. Mặc dù tôi đã phải sử dụng mã này để thành công:

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

Sau đó, tôi gọi phương thức này từ lớp 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)
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.