Skip to content

文字位置信息

准备基础类BaseView

public class BaseView extends View {
    static final int LINE_OFFSET = 60;

    protected Paint notePaint = new Paint();
    protected Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    protected Rect textRect = new Rect();

    public BaseView(Context context) {
        this(context, null);
    }

    public BaseView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BaseView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}

绘制文字的方法canvas.drawText,需要指定字符串,绘制起始坐标,画笔paint。

canvas.drawText(text, x, y, textPaint);

起始坐标的y值所在横线在后面也称为 baseline 。

获取文字边界

可以看到文字的部分笔画是可以超出边线的。比如开头的j和结尾的r字符。

使用Paint.getTextBounds方法获取文字的边界。

textPaint.getTextBounds(onShowText, 0, onShowText.length(), textRect); // 获取text边界

绘制上图的部分代码

    private void drawBounds(Canvas canvas, float tx, float ty, String onShowText) {
        canvas.drawText(onShowText, tx, ty, textPaint); // 写字
        textPaint.getTextBounds(onShowText, 0, onShowText.length(), textRect); // 获取text边界

        notePaint.setStrokeWidth(3);
        notePaint.setColor(Color.parseColor("#EF6C00"));

        // 左边线
        canvas.drawLine(tx, ty - textRect.height() - LINE_OFFSET, tx, ty + LINE_OFFSET, notePaint);

        // 右边线
        canvas.drawLine(tx + textRect.width(), ty - textRect.height() - LINE_OFFSET, tx + textRect.width(), ty + LINE_OFFSET, notePaint);

        notePaint.setColor(Color.parseColor("#FF0277BD"));
        // 上边线
        canvas.drawLine(tx - LINE_OFFSET, ty - textRect.height(), tx + textRect.width() + LINE_OFFSET, ty - textRect.height(), notePaint);

        notePaint.setColor(Color.parseColor("#00695C"));
        // y - baseline
        canvas.drawLine(tx - LINE_OFFSET, ty, tx + textRect.width() + LINE_OFFSET, ty, notePaint);
    }

获取text尺寸信息

需要使用Paint.FontMetrics类。它有5个属性 - top 从baseline到最高的文字顶部的最大距离 - ascent 单行距(singled spaced)时,baseline上方空间的推荐距离 - descent 单行距时,baseline下方空间的推荐距离 - bottom baseline到最下方字符的最大距离 - leading 多行文字之间推荐使用的额外空间

使用Paint.getFontMetrics()方法获取Paint.FontMetrics

Paint.FontMetrics fm = textPaint.getFontMetrics();

注意,top和ascent是负值。

绘制上图的方法

    private void drawFontMetrics(Canvas canvas, float x, float y, String text) {
        canvas.drawText(text, x, y, textPaint);
        textPaint.getTextBounds(text, 0, text.length(), textRect);
        Paint.FontMetrics fm = textPaint.getFontMetrics();

        notePaint.setStrokeWidth(1);
        notePaint.setColor(Color.BLACK);
        canvas.drawText(String.format(Locale.CHINA, "top:%.2f, bottom:%.2f", fm.top, fm.bottom), x, y + notePaint.getTextSize() * 2.5f, notePaint);
        canvas.drawText(String.format(Locale.CHINA, "ascent:%.2f, descent:%.2f, leading:%.2f", fm.ascent, fm.descent, fm.leading), x, y + notePaint.getTextSize() * 4f, notePaint);

        notePaint.setColor(Color.parseColor("#FFD84315"));

        // fm top线
        canvas.drawLine(x - L_BIAS, y + fm.top, x + textRect.width() + L_BIAS, y + fm.top, notePaint);

        notePaint.setColor(Color.parseColor("#FF00695C"));

        // fm bottom线
        canvas.drawLine(x - L_BIAS, y + fm.bottom, x + textRect.width() + L_BIAS, y + fm.bottom, notePaint);

        notePaint.setColor(Color.parseColor("#4527A0"));

        // fm ascent线
        canvas.drawLine(x - L_BIAS, y + fm.ascent, x + textRect.width() + L_BIAS, y + fm.ascent, notePaint);

        notePaint.setColor(Color.parseColor("#0E0822"));

        // fm descent线
        canvas.drawLine(x - L_BIAS, y + fm.descent, x + textRect.width() + L_BIAS, y + fm.descent, notePaint);
    }

对齐文字中间

有了上面的基础,我们可以很快知道如何实现文字“居中对齐”。将文字的水平中心线或竖直中心线对齐到某个点。

例如绘制一个平面坐标系

绘制出2个坐标轴,然后绘制上刻度,并写上数值。 数值文字对应到刻度相应的位置上。我们主要使用Paint.getTextBounds获取到文字的尺寸信息,再计算出合适的位置。

    private void drawChart(Canvas canvas) {
        final float x0 = 100;
        final float y0 = 300; // 原点在画布上的坐标
        final float halfLine = 5; // 刻度长度的一半

        textPaint.setColor(Color.BLACK);
        textPaint.setTextSize(20);
        notePaint.setStrokeWidth(2);
        canvas.drawLine(x0, y0, x0 + 500, y0, notePaint); // 绘制横轴
        canvas.drawLine(x0, y0, x0, y0 - 290, notePaint); // 绘制纵轴

        // 绘制横轴刻度和数字
        for (int i = 1; i <= 4; i++) {
            int step = i * 100;
            String n = String.valueOf(step);
            float x = x0 + step;
            canvas.drawLine(x, y0 - halfLine, x, y0 + halfLine, notePaint); // 刻度

            // 文字对齐
            textPaint.getTextBounds(n, 0, n.length(), textRect);
            canvas.drawText(n, x - textRect.width() / 2f, y0 + textRect.height() + halfLine, textPaint);
        }


        // 绘制纵轴刻度和数字
        for (int i = 1; i <= 2; i++) {
            int step = i * 100;
            String n = String.valueOf(step);
            float y = y0 - step;
            canvas.drawLine(x0 - halfLine, y, x0 + halfLine, y, notePaint); // 刻度

            // 文字对齐
            textPaint.getTextBounds(n, 0, n.length(), textRect);
            canvas.drawText(n, x0 - halfLine * 2 - textRect.width(), y + textRect.height() / 2f, textPaint);
        }

        // 绘制原点 0
        String origin = "0";
        textPaint.getTextBounds(origin, 0, origin.length(), textRect);
        canvas.drawText(origin, x0 - textRect.width(), y0 + textRect.height(), textPaint);
    }