[英]Android Center text on canvas
我正在尝试使用下面的代码显示文本。 问题是文本没有水平居中。 当我为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);
}
请尝试以下操作:
Paint textPaint = new Paint();
textPaint.setTextAlign(Paint.Align.CENTER);
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);
以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(矩形)来代替。 (不要使用Canvas.getClipBounds().getHeight()因为它为Rect分配内存。)
出于性能原因,您应该在onDraw()中使用对象之前分配对象。 由于drawCenter()将在onDraw()内被调用,因此对象Rect 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);
}
}
垂直对齐很困难,因为文本下降和上升发生了,很多人使用Paint.getTextBounds()来检索 TextWidth 和 TextHeight,但它并没有使文本居中。 这里我们可以使用Paint.measureText()来计算TextWidth,TextHeight我们简单的用descent和ascent做减法,然后我们得到了最接近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);
顺便说一下,我们强烈推荐使用RectF而不是Rect因为位置需要更准确的值,根据我的经验,RectF 在 xhdpi 设备上只完成了一个像素的顶部和底部偏差,Rect 将是两个更多。
您的代码在视图的中心绘制文本基线的中心。 为了在某些时候,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);
}
我发现居中文本的最佳解决方案如下:
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); `
适合我使用: textPaint.textAlign = Paint.Align.CENTER with 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
)
}
结果是:
我创建了一个方法来简化这个:
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就是你要画文字的区域,就是这样。 细节
这对我有用:
paint.setTextAlign(Paint.Align.CENTER);
int xPos = (newWidth / 2);
int yPos = (newHeight / 2);
canvas.drawText("Hello", xPos, yPos, paint);
如果有人发现任何问题,请告诉我
就我而言,我不必将文本放在画布中间,而是放在旋转的轮子中。 虽然我必须使用此代码才能成功:
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)
}
在您的油漆属性中使用它:
textPaint.setTextAlign(Paint.Align.CENTER);
如果我们使用静态布局
mStaticLayout = new StaticLayout(mText, mTextPaint, mTextWidth,
Layout.Alignment.ALIGN_CENTER, 1.0f, 0, true);
Layout.Alignment.ALIGN_CENTER这可以解决问题。 静态布局还有很多其他优点。
参考: Android 文档
将这些添加到您的 onDraw 方法中:
paint.setColor(getContext().getResources().getColor(R.color.black));
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("Text", (float) getHeight() / 2f, (float) getWidth() / 2f, paint);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.