캔버스의 Android 센터 텍스트


191

아래 코드를 사용하여 텍스트를 표시하려고합니다. 문제는 텍스트가 가로 중앙에 있지 않다는 것입니다. 에 대한 좌표를 drawText설정하면이 위치에서 텍스트의 아래쪽을 설정합니다. 텍스트가 가로로 가운데에 오도록 텍스트를 그립니다.

이것은 내 문제를 더 자세히 보여주는 그림입니다.

스크린 샷

@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
onDraw 이벤트 내에서 페인트 객체를 선언하지 않습니다. 다시 그릴 때마다 다시 작성됩니다. 개인 클래스 변수로 고려하십시오.
Christopher Rathgeb

8
항해사! 이미지 링크는 NSFW입니다. 건전하지 않다는 뜻은 아니지만 사무실에서 화면에 토플리스 여성이 등장하는 광고는 필요하지 않습니다.
Michael Scheper

5
@MichaelScheper 죄송합니다. 링크를 업데이트했습니다!
Sebastian

23
@Sebastian 이봐, 아직도 그 링크가있어?
S.Lukas

@ S.Lukas hhahaha
BOTJr.

답변:


378

다음을 시도하십시오 :

 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
좋은 대답입니다. 필자는 중앙 위치에서 시작하기 위해 텍스트 대신 가로로 가운데에 가운데를 맞추기 위해 다음을 사용했습니다. int xPos = (Width-textPaint.TextSize * Math.Abs ​​(_text.Length / 2)) / 2; 이것을 달성하는 더 좋은 방법이 있는지 확실하지 않습니다.
paj7777

그리고 아마도 _text.Length를 플로트로 캐스팅하는 것이 가장 좋습니다. 홀수의 텍스트 길이에서는 작동하지 않을 것입니다.
paj7777

61
paj7777, textPaint.setTextAlign (Align.CENTER);를 설정하면 필요하지 않습니다.
Costi Muraru

@ paj7777 그것은 고정 너비 글꼴에서만 작동했을 것입니다. 또한 플로트 에 캐스트 할 필요가 없습니다 . float로 나누면 결과는 float입니다. 예를 들어 float halfLength = text.length() / 2f;유형 프로모션 이라고합니다 .
Michael Scheper 2012

2
나는이 접근법 (= center with Paint.descent()and Paint.ascent())을 Paint.getTextBounds()아래 답변에서 텍스트 중심에 맞추는 접근법 과 비교했습니다 . Paint.descent()Paint.ascent()계정에 실제 텍스트를하지 않습니다. (아래 게시물의 스크린 샷 에서이 부정확성을 인식 할 수 있습니다.) 따라서이 접근법을 권장하지 않습니다. 접근 방식이 Paint.getTextBounds()더 정확 해 보입니다.
andreas1724

217

와 센터 Paint.getTextBounds () :

여기에 이미지 설명을 입력하십시오

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 는 텍스트의 기준점이 세로 중앙에 있다는 것을 의미하지는 않습니다. 참조 점은 항상 기준선에 있습니다. 따라서 Paint.Align.LEFT를 사용하지 않는 이유는 무엇 입니까? 어쨌든 기준점을 계산해야합니다.

  • Paint.descent () 는 실제 텍스트를 고려하지 않는다는 단점이 있습니다. Paint.descent () 는 텍스트에 하강 문자가 포함되어 있는지 여부에 관계없이 동일한 값을 검색합니다. 그래서 r.bottom을 대신 사용 합니다.

  • 좀 있었다 문제Canvas.getHeight ()를 하는 경우 API <16의 왜 내가 사용하는 것이 Canvas.getClipBounds (사각형) 대신합니다. ( Rect에 메모리를 할당 할 때 Canvas.getClipBounds (). getHeight () 를 사용하지 마십시오 .)

  • 성능상의 이유로 onDraw () 에서 사용하기 전에 객체를 할당해야합니다 . 마찬가지로 drawCenter는 () 내에서 호출 될 의 onDraw () 객체 사각형의 R은 여기 필드로 미리 할당된다.


두 가지 최고의 답변의 코드를 내 코드 (2015 년 8 월)에 넣고 결과를 비교하기 위해 스크린 샷을 만들었습니다.

텍스트 중심 세 가지 버전

텍스트는 빨간색으로 채워진 사각형 안에 위치해야합니다. 내 코드는 흰색 텍스트를 생성하고 다른 두 코드는 모두 회색 텍스트를 생성합니다 (실제로 동일하고 겹칩니다). 회색 텍스트가 약간 낮고 오른쪽에 두 개가 많습니다.

이것이 내가 테스트 한 방법입니다.

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
대단히 감사합니다. 당신의 논리는 정말 많은 도움이되었습니다.
Sujay UN

대단히 감사합니다. 당신의 논리가 정말 많은 도움이되었습니다!
LiuWenbin_NO.

이것은 텍스트를 진정으로 중심에 두는 유일한 대답입니다.
Joery

64

텍스트 하강 및 상승이 발생했기 때문에 세로로 정렬하기가 어렵습니다. 많은 사람들이 Paint.getTextBounds () 를 사용 하여 TextWidth 및 TextHeight를 검색했지만 텍스트 센터를별로 만들지 못했습니다. 여기에서 Paint.measureText () 를 사용하여 TextWidth를 계산할 수 있습니다 . TextHeight는 단순히 하강과 상승으로 빼기 만하면됩니다. 그런 다음 TextSize에 가장 근접한 접근 방식을 취합니다. 다음 작업은 서로 매우 쉽습니다.

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

코드로 스크린 샷

그건 그렇고, 우리 는 위치가보다 정확한 값을 필요로하기 때문에 Rect 대신 RectF를 사용하는 것이 좋습니다 . 제 경험상 RectF는 xhdpi 장치에서 하나의 픽셀 만 상단 및 하단 편차를 수행했습니다.


쓰여진 것처럼 var "Paint"는 혼란 스럽다. 그것은 어떻게 할당됩니까? bounds.bottom에 대한 텍스트의 상승과 하강을 어떻게 알 수 있습니까?
RoundSparrow hilltx

1
예, 이에 대한 답변을 최적화했습니다. 살펴보십시오.
VinceStyling

소중한 시간을 절약 해 주셔서 감사합니다))
Andrey

16

코드가 뷰의 중심에 텍스트 기준선의 중심을 그리고 있습니다. 문자 x, y를 중심으로 텍스트를 맞추려면 텍스트 중심을 계산 하여 포인트에 배치 해야 합니다.

이 방법은 x, y 지점을 중심으로 텍스트를 그립니다. 뷰의 중앙에 전달하면 텍스트가 중앙에 그려집니다.

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 android.graphics.Paint의 텍스트를 그리는 데 사용되는
윌리엄 리드

4

텍스트를 중심에 맞추는 가장 좋은 해결책은 다음과 같습니다.

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

이것은 질문자가 텍스트를 중앙에 배치하려고 시도한 것과 똑같습니다. Paint.Align.Center 는 텍스트의 기준점이 수직으로 중앙에있는 것을 의미하지 않기 때문에 텍스트가 수직으로 중앙에 위치하지 않습니다.
andreas1724

3

: 사용에 나를 위해 작동 textPaint.textAlign = Paint.Align.CENTERtextPaint.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
            )
        }

결과는 다음과 같습니다

여기에 이미지 설명을 입력하십시오


1

이것을 단순화하는 방법을 만듭니다.

    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는 텍스트를 그리려는 영역입니다. 세부


1

정적 레이아웃을 사용하는 경우

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

Layout.Alignment.ALIGN_CENTER 이것은 트릭을 수행합니다. 정적 레이아웃에도 많은 장점이 있습니다.

참조 : Android 설명서


1

이것은 나를 위해 일했다 :

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

누군가 문제를 발견하면 저에게 알려주세요


중심점에서 텍스트를 작성하기 시작하지만 전체 텍스트는 중심에 있지 않습니다
M Shaban Ali

1

필자의 경우 텍스트를 캔버스 중앙에 배치 할 필요가 없었지만 회전하는 휠에 넣을 필요가 없었습니다. 성공하려면이 코드를 사용해야했지만

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

그런 다음 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)
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.