美文网首页Android开发经验谈Android开发Android技术知识
Android自定义View之Paint绘制文字和线

Android自定义View之Paint绘制文字和线

作者: AntDream | 来源:发表于2018-07-30 17:35 被阅读12次

    用继承View的方式来自定义View,我们就需要重写onDraw方法,也就是得咱自己来画图了。画图就得用到画笔和画布,也就是Paint和Canvas。我们先来了解下Paint。

    Paint

    Paint我们可以简单理解为画笔或是PS里的油漆桶,也就是实际上需要设置比如颜色、粗细、字体大小等属性的对象。我们在通过继承View来自定义View时,就是通过设置Paint的属性来控制我们画出来的View的一些特性。

    Paint的一些常见API

    Paint的set相关的API

    1.设置文字的对齐方式:setTextAlign()

    //设置Paint的文字对齐方式
    textPaint = new Paint();
    textPaint.setTextAlign(Paint.Align.RIGHT);
    ...
    
    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Log.e(TAG, "onDraw");
        
        //文字的起点为(getWidth()/2,getHeight()/2)
        canvas.drawText(text, getWidth()/2,getHeight()/2,textPaint);
    }
    

    对齐方式有左中右三种

    //对齐方式有左中右三种
    public enum Align {
        /**
         * The text is drawn to the right of the x,y origin
         */
        LEFT    (0),
        /**
         * The text is drawn centered horizontally on the x,y origin
         */
        CENTER  (1),
        /**
         * The text is drawn to the left of the x,y origin
         */
        RIGHT   (2);
    
        private Align(int nativeInt) {
            this.nativeInt = nativeInt;
        }
        final int nativeInt;
    }
    

    需要注意的是,这里的对齐方式指的是和绘制原点的对齐方式,也就是上面canvas.drawText方法中我们设置的绘制起点。比如我们设置的是右对齐,那就是文字的右边和绘制起点对齐,具体效果可以看图

    设置了右对齐后

    2.设置Paint的颜色、字体大小和字体

    //这里设置Paint的颜色后,如果绘制的是字体那就是字体颜色,如果绘制的线条,那就是线条的颜色
    textPaint.setColor(ContextCompat.getColor(getContext(), R.color.colorAccent));
    textPaint.setTextSize(80f);
    //设置字体加粗
    textPaint.setTypeface(Typeface.DEFAULT_BOLD);
    
    

    字体样式有很多,除了常见的加粗,还有斜体等。这些都在Typeface类中

    public class Typeface {
        
        //字体样式,除了加粗,其他的好像看不出多大的区别
        public static final Typeface DEFAULT_BOLD;
        /** The NORMAL style of the default sans serif typeface. */
        public static final Typeface SANS_SERIF;
        /** The NORMAL style of the default serif typeface. */
        public static final Typeface SERIF;
        /** The NORMAL style of the default monospace typeface. */
        public static final Typeface MONOSPACE;
        
        // Style
        public static final int NORMAL = 0;
        //加粗
        public static final int BOLD = 1;
        //倾斜
        public static final int ITALIC = 2;
        //加粗并倾斜
        public static final int BOLD_ITALIC = 3;
    }
    

    设置加粗、倾斜

    Typeface font = Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD_ITALIC);
    textPaint.setTypeface(font);
    

    Typeface类还提供了加载自定义字体的API

    //从assets资源中加载
    Typeface createFromAsset(AssetManager mgr, String path)
    //从文件中加载字体
    Typeface createFromFile(String path)
    public static Typeface createFromFile(File path)
    

    3.设置Paint的style

    textPaint.setStyle(Paint.Style.FILL);
    

    style有3种,分别为实心、空心和实心描边。

    public enum Style {
        //实心
        FILL            (0),
        
        //空心
        STROKE          (1),
        
        //实心描边
        FILL_AND_STROKE (2);
    
        Style(int nativeInt) {
            this.nativeInt = nativeInt;
        }
        final int nativeInt;
    }
    

    测试发现对文字没啥影响,但是对线会有影响,比如如果我们设置了文字的下划线,那Fill就是实线,而设置STROKE就会变成空心的一条线

    下划线变成空心的了

    4.设置缩放:setTextScaleX(float scaleX)。scaleX范围在0-1之间为缩小,大于1为放大

    5.设置下划线和删除线有2种方式,一种是直接调用Paint的API,一种是直接设置Paint的Flag

    1) 直接通过Paint的API设置文字的下划线和删除线

    //设置下划线
    textPaint.setUnderlineText(true);
    //设置文字中间的删除线
    textPaint.setStrikeThruText(true);
    

    2) 用设置Paint的Flag的方式设置文字的下划线和删除线

    设置下划线
    textPaint.setFlags(Paint.UNDERLINE_TEXT_FLAG);
    //设置文字中间的删除线
    textPaint.setFlags(Paint.STRIKE_THRU_TEXT_FLAG);
    

    需要注意的是Paint的Flag相互之间是冲突的,如果设置了多个Flag,那只有最后一个Flag会生效。像上面的例子中,通过Flag来设置文字的下划线和删除线,最后只有删除线会生效。而通过Paint的API就不会有这个问题,删除线和下划线都会同时生效。

    6.设置Paint的抗锯齿Flag。一般我们都要设置这个抗锯齿效果,否则画出来的线和文字会坑坑洼洼的,设置了以后就会很平滑。

    textPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
    

    这里也就引申出一个问题:那就是我们一搬都会设置Paint的Flag为抗锯齿效果,所以如果我们要设置文字的下划线和删除线,就只能通过Paint的API来设置了。

    文字居中的问题

    不知道大家发现没有,上面图片中的文字尽管绘制的时候设置的位置是 (getWidth()/2,getHeight()/2) 但实际效果却并没有居中

    (1)水平方向居中问题

    • 水平方向没有居中是因为文字本身有宽度,所以要先获取文字的宽度
    float textWidth = textPaint2.measureText(text);
    
    • 然后重新计算绘制的水平坐标
    canvas.drawText(text, (getWidth()-textWidth)/2,getHeight()/2,textPaint2);
    

    (2)竖直方向居中问题

    文字的绘制规则跟Paint的一个内部类有关:FontMetrics

    public static class FontMetrics {
        /**
         * The maximum distance above the baseline for the tallest glyph in
         * the font at a given text size.
         */
        public float   top;
        /**
         * The recommended distance above the baseline for singled spaced text.
         */
        public float   ascent;
        /**
         * The recommended distance below the baseline for singled spaced text.
         */
        public float   descent;
        /**
         * The maximum distance below the baseline for the lowest glyph in
         * the font at a given text size.
         */
        public float   bottom;
        /**
         * The recommended additional space to add between lines of text.
         */
        public float   leading;
    }
    
    FontMetrics参数值含义

    从图中我们可以看出,文字绘制以baseline为标准,我们将baseline设置为getHeight()/2后,文字势必会往上偏,所以我们要想让文字在竖直方向上居中,baseline需要往下一点。

    Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
    float y = getHeight()/2 + (Math.abs(fontMetrics.ascent) - fontMetrics.descent)/2;
    canvas.drawText(text, (getWidth()-textWidth)/2,y,textPaint);
    
    

    需要注意的是Paint的TextAlign属性需要去掉,否则无法准确居中

    这样子文字就居中了

    文字终于居中啦

    用Paint绘制线

    PathEffect setPathEffect(PathEffect effect)
    

    1)通过setPathEffect方法可以设置路径效果,一共可以设置7种路径效果,比较常见的有虚线和拐角圆滑效果

    //CornerPathEffect用于增加点与点之间的圆弧效果,CornerPathEffect中的参数表示圆弧效果的半径
    brokenLinePaint.setPathEffect(new CornerPathEffect(5));
    
    //DashPathEffect用于绘制虚线,第一个参数表示线段各个点之间的偏移,第二个参数表示绘制时数组的偏移量
    brokenLinePaint.setPathEffect(new DashPathEffect(new float[]{5f,5f,5f,5f}, 1f));
    

    需要注意的是Paint的PathEffect也只能设置一个,设置多个时只有最后一个设置有效

    2)Paint绘制折线需要用到一个Path类,用于存放折线的各个点

    1)初始化

    Path mPath = new Path();
    

    2)设置折线的起点

    //2个参数分别为起点的X坐标和Y坐标
    mPath.moveTo(mPaddingLeft, mHeight-mPaddingBottom);
    

    3)添加折线上的其他点

     mPath.lineTo(pointX, pointy);
    

    4)绘制

    canvas.drawPath(mPath, brokenLinePaint);
    

    总结

    • Paint是绘制View必须要掌握的,我们只需要掌握Paint的常见的API就行了,包括绘制文字的和绘制线的。
    • Paint绘制文字时需要注意文字居中的问题,水平居中需要考虑文字的宽度,竖直居中需要考虑文字绘制的baseline,这就需要注意理解FontMetrics,如果不理解的话直接记结论也行
    Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
    float y = getHeight()/2 + (Math.abs(fontMetrics.ascent) - fontMetrics.descent)/2;
    canvas.drawText(text, (getWidth()-textWidth)/2,y,textPaint);
    
    • Paint的Flag只能设置一个,设置多个时也只有最后一个有效。
    • Paint的PathEffect和Flag也一样,只能设置一个,设置多个时只有最后一个有效

    今天你进步了嘛?欢迎关注我的微信公众号,和我一起每天进步一点点!


    AntDream

    相关文章

      网友评论

        本文标题:Android自定义View之Paint绘制文字和线

        本文链接:https://www.haomeiwen.com/subject/qdlfvftx.html