Android自定义控件入门到精通--Text文本

《Android自定义控件入门到精通》文章索引 ☞ https://blog.csdn.net/Jhone_csdn/article/details/118146683

《Android自定义控件入门到精通》所有源码 ☞ https://gitee.com/zengjiangwen/Code

Text

drawText(String text, float x, float y, Paint paint)

不知道大家有没有这样的经历,在绘制文字的时候,总是把握不住绘制的精确位置,比如,我想在矩形中,居中绘制"Android"

    @Override
    protected void onDraw(Canvas canvas) {
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(2);
        mPaint.setTextSize(14);
        mPaint.setColor(Color.YELLOW);
        Rect rect = new Rect(10, 10, 120, 60);
        canvas.drawRect(rect, mPaint);
        //获取文字宽度
        float textWidth = mPaint.measureText("Android");
        Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
        //获取文字高度
        float textHeight=fontMetrics.bottom-fontMetrics.top;
        float x= rect.left+(rect.width()-textWidth)/2;
        float y=rect.top+(rect.height()-textHeight)/2;
        canvas.drawText("Android",x,y,mPaint);
    }

我们预测drawText()中的x、y为开始绘制的坐标,即猜测以上代码可以实现文字水平垂直居中,但现实往往会打脸:

在这里插入图片描述

水平是居中了,但这个垂直居中有点让人摸不着头脑啊!

在前几篇中,我们绘制各种图形,都是以左上角为起始点开始绘制,为啥到了文字绘制这,不灵了呢?

还记得小学时候抄过的字母吗?

在这里插入图片描述

本子上每行的四条线,特别是红线是不是记忆尤新?

同样的,Android中Text的绘制也有对应的四条线

    @Override
    protected void onDraw(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(1);
        mPaint.setColor(Color.YELLOW);
        mPaint.setTextSize(48);//设置文字大小为12像素
        String text = "abcdefghijklmnopqrst";
        float textWidth = mPaint.measureText(text);
        int x = 100, y = 100;
        Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
        float top = fontMetrics.top;
        float ascent = fontMetrics.ascent;
        float descent = fontMetrics.descent;
        float bottom = fontMetrics.bottom;
        mPaint.setColor(Color.YELLOW);
        canvas.drawLine(x, top + y, x + textWidth, top + y, mPaint);
        mPaint.setColor(Color.RED);
        canvas.drawLine(x, ascent + y, x + textWidth, ascent + y, mPaint);
        mPaint.setColor(Color.RED);
        canvas.drawLine(x, y, x + textWidth, y, mPaint);
        mPaint.setColor(Color.WHITE);
        canvas.drawLine(x, descent + y, x + textWidth, descent + y, mPaint);
        mPaint.setColor(Color.GREEN);
        canvas.drawLine(x, bottom + y, x + textWidth, bottom + y, mPaint);
        mPaint.setColor(Color.WHITE);
        canvas.drawText(text, x, y, mPaint);
    }

在这里插入图片描述

可以看到,y的坐标就是作业本上的红线,我们称之为基线(baseLine)

  • fontMetrics.top:基线到top线的距离,为负数
  • fontMetrics.ascent:基线到ascent线的距离,为负数
  • fontMetrics.descent:基线到descent线的距离,为正数
  • fontMetrics.bottom:基线到bottom线的距离,为正数

我们可以明确下以下几个知识点:

  • 文字的字高=descent-ascent
  • 文字的行高=bottom-top
  • 文字的宽度可以通过Paint.measureText(text)计算
  • 文字绘制的起点(x,y)为基点,基点的y坐标称为基线
  • 文字的基线=垂直中线+(字高/2-bottom)

在这里插入图片描述

明白了这些,我们再来居中绘制我们的"Android"

    @Override
    protected void onDraw(Canvas canvas) {
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(1);
        mPaint.setColor(Color.YELLOW);
        Rect rect = new Rect(10, 10, 120, 60);
        canvas.drawRect(rect, mPaint);
        mPaint.setTextSize(12);//设置文字大小为12像素
        String text = "Android";
        float textWidth = mPaint.measureText(text);
        Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
        float top = fontMetrics.top;
        float bottom = fontMetrics.bottom;
        float baseLineX = (rect.right - rect.left - textWidth) / 2 + rect.left;
        float offset = (bottom - top) / 2 - bottom;//文字中间到基线的距离
        float baseLineY = (rect.bottom - rect.top) / 2 + offset + rect.top;
        canvas.drawText(text, baseLineX, baseLineY, mPaint);
    }

在这里插入图片描述
baseY=-(fontMetrics.bottom+fontMetrics.top)/2

Paint.setTextAlign(Paint.Align)

文本的对齐方式

    public enum Align {
        LEFT    (0),//基点左对齐
        CENTER  (1),//基点居中对齐
        RIGHT   (2);//基点又对齐
    }

啥意思呢,我们直接看例子

Align.LEFT

在这里插入图片描述

Align.CENTER

在这里插入图片描述

Align.RIGHT

在这里插入图片描述

drawTextOnPath(String text, Path path, float hOffset,float vOffset, Paint paint)

沿着路劲绘制文本

  • path:一条路径
  • hOffset:水平偏移量
  • vOffset:垂直偏移量
    @Override
    protected void onDraw(Canvas canvas) {
        mPaint.setColor(Color.RED);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(2);
        Path path = new Path();
        path.moveTo(40, 100);
        path.lineTo(600, 100);
        canvas.drawPath(path, mPaint);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setLetterSpacing(0.3f);
        mPaint.setTextSize(20);
        canvas.drawTextOnPath("深圳市腾讯计算机技术有限公司", path, 30, 60, mPaint);
    }

在这里插入图片描述

理解了hOffset和vOffset,我们在来看看这个

mPaint.setColor(Color.RED);//设置画笔颜色
mPaint.setStyle(Paint.Style.STROKE);//设置画笔描边
mPaint.setStrokeWidth(10);//设置线宽
mPaint.setLetterSpacing(0.3f);//设置字间距

Path path=new Path();//新建一个路径
path.addCircle(400,400,100, Path.Direction.CW);//在路径上添加一个圆,CW:顺时针

Matrix matrix=new Matrix();//新建一个Matrix矩阵,用来旋转路径
matrix.setRotate(90,400,400);//路径旋转90度
path.transform(matrix);

canvas.drawPath(path,mPaint);

String text="深圳市腾讯计算机技术有限公司";
mPaint.setStyle(Paint.Style.FILL);
mPaint.setTextSize(20);
canvas.drawTextOnPath(text,path,130,30,mPaint);

在这里插入图片描述

大家有疑惑吧,path.transform(matrix)这个用来干啥的,一个圆形路径,转来转去不是一样吗?那去掉这个代码来看看效果:

在这里插入图片描述

我们再调下hOffset的值,来达到第一个效果:

    @Override
    protected void onDraw(Canvas canvas) {
        mPaint.setColor(Color.RED);//设置画笔颜色
        mPaint.setStyle(Paint.Style.STROKE);//设置画笔描边
        mPaint.setStrokeWidth(10);//设置线宽
        mPaint.setLetterSpacing(0.3f);//设置字间距

        Path path=new Path();//新建一个路径
        path.addCircle(160,160,100, Path.Direction.CW);//在路径上添加一个圆

//        Matrix matrix=new Matrix();//新建一个Matrix矩阵,用来旋转路径
//        matrix.setRotate(90,160,160);//路径旋转90度
//        path.transform(matrix);
        canvas.drawPath(path,mPaint);

        String text="深圳市腾讯计算机技术有限公司";
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setTextSize(20);
        canvas.drawTextOnPath(text,path,300,30,mPaint);
    }

在这里插入图片描述

发现了吧,“公司”这两个字重叠了,如果hOffset的值继续调大,可能就是这么个效果

在这里插入图片描述

结论:文字超出路径(圆周长)的范围是会重叠在一起显示!

这时候,我们再来看看hOffset和vOffset两个值在这个例子中是在什么位置:

在这里插入图片描述

  • hOffset指的是0的位置到文字起点的位置(在圆周长400的位置)
  • vOffset指的是文字与圆弧垂直方向的距离
  • hOffset=400,再加上文本的长度,已经超出了圆周长,所以后面的文字重叠了

所以,我们需要旋转路径,改变它的起点。

整个例子的代码

    @Override
    protected void onDraw(Canvas canvas) {
        mPaint.setColor(Color.RED);//设置画笔颜色
        mPaint.setStyle(Paint.Style.STROKE);//设置画笔描边
        mPaint.setStrokeWidth(10);//设置线宽
        mPaint.setLetterSpacing(0.3f);//设置字间距

        Path path = new Path();//新建一个路径
        path.addCircle(160, 160, 100, Path.Direction.CW);//在路径上添加一个圆

        Matrix matrix=new Matrix();//新建一个Matrix矩阵,用来旋转路径
        matrix.setRotate(90,160,160);//路径旋转90度
        path.transform(matrix);
        canvas.drawPath(path, mPaint);

        String text = "深圳市腾讯计算机技术有限公司";
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setTextSize(20);
        canvas.drawTextOnPath(text, path, 130, 30, mPaint);

        String text1 = "南山总部";
        Path path1 = new Path();
        path1.addCircle(160, 160, 100, Path.Direction.CCW);
        canvas.drawTextOnPath(text1, path1, 420, -20, mPaint);

        //画五角星
        double arcAngle = (72 * Math.PI) / 180;
        float pointX[] = new float[5], pointY[] = new float[5];
        //以五角星中心点为(0,0),定义五角星右下角一个角点(40,40),通过这个点来计算五角星的其它点坐标
        pointX[0] = 40;
        pointY[0] = 40;
        for (int i = 1; i < 5; i++) {
            pointX[i] = (int) (pointX[i - 1] * Math.cos(arcAngle) - pointY[i - 1] * Math.sin(arcAngle));
            pointY[i] = (int) (pointY[i - 1] * Math.cos(arcAngle) + pointX[i - 1] * Math.sin(arcAngle));
        }

        Path star = new Path();
        star.moveTo(pointX[0], pointY[0]);
        for (int i = 0; i < 5; i += 2) {
            star.lineTo(pointX[i], pointY[i]);
        }
        for (int i = 1; i < 5; i += 2) {
            star.lineTo(pointX[i], pointY[i]);
        }
        //将五角星的中心点移动到我们圆心的位置
        canvas.translate(160, 160);
        canvas.drawPath(star, mPaint);
    }

在这里插入图片描述

drawPosText (char[] text, int index, int count, float[] pos, Paint paint)

drawPosText (String text, float[] pos, Paint paint)

  • char[] text:要绘制的文字数组
  • int index:从第几个文字开始绘制(开始位置的索引)
  • int count:绘制文字的个数,从index开始算
  • float[] pos:给每个文字定位,两个值一组,[x1,y1,x2,y2,x3,y3……]
    @Override
    protected void onDraw(Canvas canvas) {
        mPaint.setColor(Color.RED);//设置画笔颜色
        mPaint.setStyle(Paint.Style.FILL);//设置画笔描边
        canvas.drawPosText("安卓天下第一", new float[]{23, 32, 43, 21, 54, 76, 90, 100, 45, 156, 49, 123}, mPaint);
    }

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一鱼浅游

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值