Android Study之自定义View进阶路:掌握绘制基本图形(一) [复制链接]

2017-8-22 15:24
静心Study 阅读:1867 评论:8 赞:2
Tag:  android自定义viewcanvas

> LZ-Says:我觉得人与人相处,首先排在首位的是诚信,其次才是一些其他的东西。而关于技术,交流才是成长的路,闭门造车永远看不到外面的精彩,领悟不到自身新高度。So,诚信待人,多交流,多沟通,一起成长~!

前言

开发过程以及平时使用过程中,总是能看到炫之又炫的效果,以前一直都是展首相望,一直都不敢触碰,But,今天,我们从基础开始,一步一步征服自定义View。

在此,装个小B,希望大家和我一样掌握之后都可以如下这么豪气~ ^.^

这里写图片描述

本文目标

通过讲解并举例说明绘制基本图形方法技巧,get自定义View基础招式,更6,更自信的玩转自定义View。

自定义View缘由

总说自定义View,自定义View,我为什么要自定义View,系统自带的不行吗?下面我们简单的举例说明下。

> Boss Say:XXX,这个字给我闪动起来~
>
> You:好~(心里默默Fuck...)

如上很简单,却又很实际的一个场景,一般来说我们显示直接TextView加载显示内容即可,但是现在要闪动起来,那么这个时候怎么办呢?系统也没有提供相应的案例。

只能自定义了呗。

So,下次有人再问你为什么要自定义View,你可以这样告诉他:

> 当系统自带控件已经无法满足我们实际需求,这时候,就需要自定义View去实现了。

回顾常用控件

对于一名Android开发者,想必已经玩转基本同时也是常用控件的使用了吧,But,大家有没有想到它们的内部是如何实现的?或者简单说,他们都是继承谁呢?

简单为大家附上几个例子:

  • public class TextView extends View

  • public class Button extends TextView

  • public class CheckBox extends CompoundButton

  • public abstract class CompoundButton extends Button

  • public class RelativeLayout extends ViewGroup

  • public abstract class ViewGroup extends View

  • public class LinearLayout extends ViewGroup

... ... 等等诸如此类有很多,这里就不一一说明了,大家仔细观察,看看有什么相似点。

而我们自定义View,又该继承What?

LZ简单概括下~

自定义View继承父类及简述

  • 继承常用控件,在原有基础上进行升级改造;

  • 继承View;

  • 继承ViewGroup;

  • 组合控件。

下面分别举例说明以上使用场景。

1.继承常用控件,升级或改造

比如之前ScrollView嵌入ListView或者GirdView常常会出现显示不全,我们有n种解决办法,其中有一种便是继承ListView或者GirdView重写即可解决,下面贴出关键代码:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
                MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, expandSpec);
    }

这种方式的好处不用说也知道,方便,快捷。

2.继承View或者ViewGroup区别

大家都知道,ViewGroup是View的子类,那么继承它们有什么区别呢?

  • View,从字面上来看,就是显示视图,比如我画个圆,画个矩形,我操作仅仅是显示,那么我们果断继承View即可;

  • ViewGroup,同样从字面上来看,Group群组,它里面允许放置一些子视图,主要也是用户自定义一些布局什么的,可以考虑继承ViewGroup。

3.组合控件

关于这个可以考虑查看LZ之前写的如下一篇文章,地址如下:

> Android Study 之分分钟让你玩转EditText右下角实时显示输入字数

先上个效果图

这里写图片描述

组合控件,个人感觉优势在于比继承系统提供控件方便,开发起来也就是基于已有控件,难点或者说是复杂处也就是自己的逻辑。

View绘制流程

下方高能,附上一张流程图,简单了解下,进入我们撸码阶段。

这里写图片描述

图片来源:http://www.jianshu.com/p/f0bc39dbfa26

以上流程图重点关注onDraw()以及onMeasure()即可。下面简单说明下:

  • onDraw:鉴名其意,就是绘制,绘制的统统在这里,秒懂?

  • onMeasure:这个可以简单理解为绘制的区域大小,就好比你指定一个控件宽高一样。

PS:LZ可是一枚有着良好职业道德,职业操守以及职业素养的Android开发者。

基本图形绘制

这里写图片描述

> 先定一个能达到的小目标
>
> 比如说我先玩转绘制基本图形~

一、绘制圆

撸码前,明确下操作流程:

  • 继承View,实现构造,添加初始化画笔方法;

  • 实现onDraw()方法

没错,就这俩点,具体撸码瞧着~

1.继承View,实现构造函数

public class CircleView extends View {

    /**
     * 实例化时调用
     * @param context
     */
    public CircleView(Context context) {
        super(context);
    }

    /**
     * 设置属性时调用
     * @param context
     * @param attrs 属性集合
     */
    public CircleView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * 设置样式时调用
     * @param context
     * @param attrs
     * @param defStyleAttr 样式
     */
    public CircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    
}

2.改造构造,为了更好,更方便的调用,并且新增初始化画笔方法

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

    public CircleView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
    }

这里可能有人就会说了,这个和那个有什么区别,区别也不是很大嘛。

其实都差不多,改造的原因就是调用其中一个构造的同时也能调用其它,相对来说提供一些方便而已。

下面将采取方法说明+代码实现+效果方式进行讲解。

1. 万能Paint

Paint:画笔 功能强大 可设置颜色 样式等等

初始化如下:

        // 实例化画笔
        paint = new Paint();
        // 设置画笔颜色
        paint.setColor(Color.RED);

2. drawCircle()绘制圆

参数说明:

> drawCircle(float cx, float cy, float radius, Paint paint)
>
> * cx,cy:圆心x,y轴距离;
>
> * radius:圆半径;
>
> * paint:画笔

调用如下:

canvas.drawCircle(0, 0, 100, paint);

效果如下:

这里写图片描述

这时候,有人会说了,圆呢?让你糟了?画丢了?

表急,这里为大家引入一个坐标系的概念。

3. 坐标系

这里写图片描述

如上图,以屏幕左上角为中心点,也就是x,y轴值为(0,0)点。So,秒懂?下面我们指定x,y轴坐标,来看看一个完整的圆~

4. 绘制红色完整圆

指定x,y轴为300,如下:

canvas.drawCircle(300, 300, 100, paint);

效果如下:

这里写图片描述

在这里不得不提一句,关于绘制出现锯齿情况,如下小篇幅。

5. setAntiAlias()锯齿以及抗锯齿

关于这个效果,LZ找了一个看的比较明显的图,如下:

这里写图片描述

同样本部分也摘于凯哥的HenCoder,下面附上地址,方便大家学习了解。

> http://hencoder.com/ui-1-1/

关于这块,LZ学习了下凯哥的文章,简单的从里面截取一些内容,如下:

> 实质上,锯齿现象的发生,只是由于图形分辨率过低,导致人眼察觉出了画面中的像素颗粒而已;而抗锯齿的原理是:修改图形边缘处的像素颜色,从而让图形在肉眼看来具有更加平滑的感觉

下面再次盗取凯哥一张图,如下:

这里写图片描述

开启抗锯齿(开启后会消耗部分系统性能,可忽略不计):

> paint.setAntiAlias(true);

So,清晰明了,接着往下瞅~

到现在,我们已经掌握如何画一个简单的圆,对应的坐标系以及锯齿的简单知识,而下面,我们通过一些简单的方法去实现简单的效果,先达到一个初步了解的目的。

6. setStyle设置样式

参数说明:

> setStyle(Paint.Style style)中style有三个取值,分别如下:
>
> * FILL:填充内部,默认此效果;
>
> 这里写图片描述
> <br/>
> * FILL_AND_STROKE:填充内部并描边,如下效果图,是不是觉得和默认的时候一样呢?视觉效果是一样,但是中间的流程却大不一样,简单来说,Fill仅仅是填充内容即可,而此属性值却在填充的基础增加了描边;
> 这里写图片描述
> <br/>
>
> * STROKE:描边,只绘制边缘部分,中间内容不绘制,也就是我们说的空心圆。
> 这里写图片描述

调用起来也是很easy,如下:

paint.setStyle(Paint.Style.FILL_AND_STROKE);

7. setStrokeWidth设置描边宽度

参数说明:

> setStrokeWidth(float width),数值越大,描边越宽
>
> 这里写图片描述

简单调用如下:

paint.setStrokeWidth(30);

8. drawOval()绘制椭圆

> drawOval(),拥有俩种参数方式,如下:
>
> * drawOval(float left, float top, float right, float bottom, Paint paint)
> 参数含义分别为:左上右下坐标以及画笔
> <br/>
> * drawOval(RectF oval, Paint paint)
> 参数含义为左上右下点集合(封装类)以及画笔

下面附上简单使用方式:

        // 绘制椭圆
        rectF=new RectF();
        rectF.left=100;
        rectF.top=600;
        rectF.right=400;
        rectF.bottom=800;
        canvas.drawOval(rectF,paint);

效果如下:

这里写图片描述

到此,圆的绘制暂时告一段落,下面开启矩形绘制,其实都差不多,不信瞧着~

二、绘制矩形

1. drawRect()绘制矩形:

> drawRect()绘制矩形,提供了三种方式,分别如下:
>
> * drawRect(float left, float top, float right, float bottom, Paint paint)
> left,top,right,bottom:左上右下点;
> paint:画笔。
> <br/>
> * drawRect(Rect r, Paint paint)
> r:左上右下点集合;
> paint:画笔。
> <br/>
> * drawRect(RectF rect, Paint paint)
> rect:左上右下点集合;
> paint:画笔。

下面依次附上调用以及效果:

        // 三种方式画矩形
        paint.setColor(Color.RED);
        // 左上右下
        canvas.drawRect(20, 20, 300, 300, paint);

        paint.setColor(Color.BLUE);
        rect = new Rect(320, 20, 600, 300);
        canvas.drawRect(rect, paint);

        paint.setColor(Color.BLACK);
        rectF = new RectF();
        rectF.left = 620;
        rectF.top = 20;
        rectF.right = 900;
        rectF.bottom = 300;
        canvas.drawRect(rectF, paint);

效果如下:

这里写图片描述

2. 设置空心以及描边宽度

方式和绘制圆一样,这里直接贴关键代码了。

        // 设置矩形空心
        strokePaint.setColor(Color.RED);
        strokePaint.setStyle(Paint.Style.STROKE);
        canvas.drawRect(20, 320, 300, 600, strokePaint);

        // 设置矩形描边宽度
        strokeWPaint.setColor(Color.RED);
        strokeWPaint.setStyle(Paint.Style.STROKE);
        strokeWPaint.setStrokeWidth(20);
        canvas.drawRect(20, 620, 300, 900, strokeWPaint);

效果如下:

这里写图片描述

3. drawRoundRect()绘制圆角矩形

感觉这个更简单了,So easy了,还是老规矩,提下方法参数:

> drawRoundRect():同样提供了俩种方式,但是原理都是一样,如下:
>
> * drawRoundRect(RectF rect, float rx, float ry, Paint paint)
> rect:左上右下点集合;
> rx:x轴方向边缘弧度;
> ry:y轴方向边缘弧度;
> paint:画笔。
> <br/>
> * drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint)
> 和上面基本一致,只不过点集合变成了一个个具体点了。

贴代码呗。

        // 绘制圆角矩形
        roundPaint.setColor(Color.BLUE);
        roundPaint.setStyle(Paint.Style.STROKE);
        roundPaint.setStrokeWidth(2);
        canvas.drawRoundRect(320, 320, 600, 600, 50, 50, roundPaint);

        // 绘制圆角矩形
        roundPaint.setColor(Color.BLUE);
        roundPaint.setStyle(Paint.Style.STROKE);
        roundPaint.setStrokeWidth(2);
        canvas.drawRoundRect(320, 620, 600, 900, 50, 20, roundPaint);

来个效果瞅瞅~

这里写图片描述

OK~

三、绘制点 线 弧线 扇形

1. drawLine()绘制线

绘制单条线:

> drawLine(float startX, float startY, float stopX, float stopY, Paint paint):
>
> * startX:起始点x轴坐标;
> * startY:起始点y轴坐标;
> * stopX:结束点x轴坐标;
> * stopY:结束点y轴坐标;
> * paint:画笔

绘制多条线:

> drawLines(float[] pts, Paint paint):
>
> * pts:坐标集合,每组包含4个坐标点,分别为起始点x轴坐标,起始点y轴坐标,结束点x轴坐标和结束点y轴坐标;
> * paint:画笔

指定绘制点集合:

> drawLines(float[] pts, int offset, int count, Paint paint):
>
> * pts:绘制坐标点集合;
> * offset:指定哪儿些点不需要绘制;
> * count:实际需要绘制的点;
> * paint:画笔

下面依次贴出部分代码,首先绘制一条线:

        // 画单条线
        canvas.drawLine(100, 50, 500, 50, paint);

效果:

这里写图片描述

绘制一个字母,^_^

    // 多个坐标集合 一组四个坐标 分别为起始x轴,起始y轴,结束x轴,结束y轴
    private float[] pts = {
            100, 200, 100, 600,
            100, 400, 300, 400,
            300, 200, 300, 600};
            
    // 绘制几条线
    canvas.drawLines(pts, paint);

瞅瞅效果?
这里写图片描述

绘制指定点,这里直接绘制所有点:

    private float[] pts1 = {
            400, 100, 400, 200,
            400,100,600,100,
            400,200,600,200};
            
    // 参数说明
    // pts:绘制直线的端点数组,每条直线占用4个数据。
    // offset:跳过的数据个数,这些数据将不参与绘制过程。
    // count:实际参与绘制的数据个数。
    // paint:绘制直线所使用的画笔。
    canvas.drawLines(pts1,0,12,paint);

效果如下:
这里写图片描述

2. drawArc()绘制扇形 弧形

同样提供俩种方式:

> drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint):
>
> * oval:点坐标集合,指定圆弧的外轮廓矩形区域;
> * startAngle:起始角度;
> * sweepAngle:经过的角度,顺时针方向,从右中间开始为零度;
> * useCenter:如果为True时,绘制圆弧时将圆心包括在内,通常用来绘制扇形,反之则是弧形;
> * paint:画笔。
> drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint)
> 同样,只是点坐标集合变成了单个

        // 绘制弧线 false为弧线 true为扇形
        rectF=new RectF();
        rectF.left=20;
        rectF.top=600;
        rectF.right=220;
        rectF.bottom=800;
        paint.setStyle(Paint.Style.STROKE);
        canvas.drawArc(rectF,0,90,false,paint);

        rectF=new RectF();
        rectF.left=20;
        rectF.top=800;
        rectF.right=220;
        rectF.bottom=1000;
        paint.setStyle(Paint.Style.STROKE);
        canvas.drawArc(rectF,0,90,true,paint);

展示下效果:

这里写图片描述

3. 绘制点

这个最是Easy

就直接贴代码了,具体大家可以自己看看源码:

        // 绘制点
        canvas.drawPoint(600,500,paint);

如下效果:
这里写图片描述

四、绘制文字

这个感觉So Easy,就直接贴出部分源码以及效果了。

        // 设置文本大小
        paint.setTextSize(50);
        // 绘制文字
        canvas.drawText("Hello Custom View",100,100,paint);

        // 绘制指定长度文本
        canvas.drawText("Hello,妹子,你好~",6,11,100,200,paint);

这里写图片描述

GitHub查看地址

> https://github.com/HLQ-Struggle/AndroidCustomView

个人感触

> 其实我觉得我们应该去感恩,感恩无私奉献的Android团队,感谢开源的Android伙伴,我们真的是站在巨人的肩膀上去看世界,所以我们有什么理由和借口不去努力?不去为了Android越来越好而做出自己的微小贡献呢?

结束

可能有的人会说了,这些东西很简单,直接一个类放上去不就好了,何必这么麻烦?

确实,这样做是我省事儿了,但是从不会到会是一个过程,虽然这样写的很麻烦,但是我感觉像和LZ一样的想学习的Android小伙伴会看的清晰明了,So,我麻烦点就麻烦点吧。

感谢大家查阅,不足之处欢迎提出,随时修改,完善~

参考文献

首选谷歌Baba官方文档,地址如下:
> https://developer.android.google.cn/reference/android/graphics/Canvas.html

分享到:
我来说两句
facelist
您需要登录后才可以评论 登录 | 立即注册
所有评论(8)
权小阳 2017-8-23 10:46
静心不要告诉我 你这是为了参加9月份比赛的文章
回复
静心Study 2017-8-24 09:58
权小阳: 静心不要告诉我 你这是为了参加9月份比赛的文章
嗯 提前准备了 比较简单 easy
回复
权小阳 2017-8-25 11:29
静心Study: 嗯 提前准备了 比较简单 easy
说不定我规则变了哦~
回复
静心Study 2017-8-25 11:55
权小阳: 说不定我规则变了哦~
木事 重在参与嘛
回复
权小阳 2017-8-25 13:06
静心Study: 木事 重在参与嘛
棒棒哒~
回复
huyawen 2017-8-25 17:40
我是来赚安币的  哈哈哈
回复
暴雨2016 2017-9-8 10:10
666
回复
随风而逝无痕 2018-3-3 16:08
学习了,厉害!
回复

领先的中文移动开发者社区
18620764416
7*24全天服务
意见反馈:1294855032@qq.com

扫一扫关注我们

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 粤ICP备15117877号 )