首页 文章

画布上的Android中心文字

提问于
浏览
160

我正在尝试使用下面的代码显示文本 . 问题是文本不是水平居中的 . 当我设置 drawText 的坐标时,它会在此位置设置文本的底部 . 我希望绘制文本,以便文本也水平居中 .

这是一张进一步显示我的问题的图片:

Screenshot

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

9 回答

  • 171

    请尝试以下方法:

    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);
    
  • 59

    中心 Paint.getTextBounds()

    enter image description here

    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 的原因 .

    • 如果API <16,我有problemsCanvas.getHeight() 这就是我使用 Canvas.getClipBounds(Rect) 的原因 . (不要使用 Canvas.getClipBounds().getHeight() ,因为它为 Rect 分配内存 . )

    • 出于性能原因,您应该在 onDraw() 中使用对象之前对其进行分配 . 由于 drawCenter() 将在 onDraw() 内被调用,因此对象 Rect r 在此处被预先分配为字段 .


    我试着将两个顶级答案的代码放入我自己的代码中(2015年8月)并制作了一个截图来比较结果:

    text centered three versions

    文本应位于红色填充矩形的中心 . 我的代码产生白色文本,另外两个代码产生灰色文本(它们实际上是相同的,重叠的) . 灰色文字有点太低,右边有两个 .

    这就是我进行测试的方式:

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

    垂直对齐是很困难的,因为文本下降和上升发生了,很多人使用 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);
    

    screen shot by the code

    顺便说一句,我们强烈建议使用 RectF 而不是 Rect ,因为位置需要更准确的值,根据我的经验,RectF在xhdpi设备上完成顶部和底部偏差只有一个像素,Rect会多两个 .

  • 3

    您的代码正在绘制文本基线的中心,位于视图的中心 . 为了使文本在某个点(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);
    }
    
  • 1

    我发现以文本为中心的最佳解决方案如下:

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

    我创建了一个方法来简化这个:

    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是你想要绘制文本的区域,就是这样 . Details

  • 1

    如果我们使用静态布局

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

    Layout.Alignment.ALIGN_CENTER 这将成功 . 静态布局还有很多其他优点 .

    参考:Android Documentation

  • 0

    这对我有用:

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

    如果有人发现任何问题,请告诉我

  • 13

    适用于我: 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
                )
            }
    

    结果是:

    enter image description here

相关问题