原标题:Android Study之自定义View进阶路:掌握绘制基本图形(一)
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重写即可解决,下面贴出关键代码:
@OverrideprotectedvoidonMeasure(intwidthMeasureSpec, intheightMeasureSpec){
intexpandSpec = 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绘制流程
下方高能,附上一张流程图,简单了解下,进入我们撸码阶段。
以上流程图重点关注onDraw()以及onMeasure()即可。下面简单说明下:
onDraw:鉴名其意,就是绘制,绘制的统统在这里,秒懂?
onMeasure:这个可以简单理解为绘制的区域大小,就好比你指定一个控件宽高一样。
PS:LZ可是一枚有着良好职业道德,职业操守以及职业素养的Android开发者。
基本图形绘制
> 先定一个能达到的小目标
> 比如说我先玩转绘制基本图形~
一、绘制圆
撸码前,明确下操作流程:
继承View,实现构造,添加初始化画笔方法;
实现onDraw()方法
没错,就这俩点,具体撸码瞧着~
1.继承View,实现构造函数
publicclassCircleViewextendsView{
/** * 实例化时调用 * @paramcontext */publicCircleView(Context context){
super(context); }
/** * 设置属性时调用 * @paramcontext * @paramattrs 属性集合 */publicCircleView(Context context, @Nullable AttributeSet attrs){
super(context, attrs); }
/** * 设置样式时调用 * @paramcontext * @paramattrs * @paramdefStyleAttr 样式 */publicCircleView(Context context, @Nullable AttributeSet attrs, intdefStyleAttr){
super(context, attrs, defStyleAttr); } }
2.改造构造,为了更好,更方便的调用,并且新增初始化画笔方法
publicCircleView(Context context){
this(context, null); }
publicCircleView(Context context, @Nullable AttributeSet attrs){
this(context, attrs, 0); }
publicCircleView(Context context, @Nullable AttributeSet attrs, intdefStyleAttr){
super(context, attrs, defStyleAttr); initPaint(); }
这里可能有人就会说了,这个和那个有什么区别,区别也不是很大嘛。
其实都差不多,改造的原因就是调用其中一个构造的同时也能调用其它,相对来说提供一些方便而已。
下面将采取方法说明+代码实现+效果方式进行讲解。
1. 万能Paint
Paint:画笔 功能强大 可设置颜色 样式等等
初始化如下:
// 实例化画笔paint = newPaint();
// 设置画笔颜色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:填充内部,默认此效果;
> * FILL_AND_STROKE:填充内部并描边,如下效果图,是不是觉得和默认的时候一样呢?视觉效果是一样,但是中间的流程却大不一样,简单来说,Fill仅仅是填充内容即可,而此属性值却在填充的基础增加了描边;
> * 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)
> 参数含义分别为:左上右下坐标以及画笔
> * drawOval(RectF oval, Paint paint)
> 参数含义为左上右下点集合(封装类)以及画笔
下面附上简单使用方式:
// 绘制椭圆rectF=newRectF(); 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:画笔。
> * drawRect(Rect r, Paint paint)
> r:左上右下点集合;
> paint:画笔。
> * drawRect(RectF rect, Paint paint)
> rect:左上右下点集合;
> paint:画笔。
下面依次附上调用以及效果:
// 三种方式画矩形paint.setColor(Color.RED);
// 左上右下canvas.drawRect(20, 20, 300, 300, paint); paint.setColor(Color.BLUE); rect = newRect(320, 20, 600, 300); canvas.drawRect(rect, paint); paint.setColor(Color.BLACK); rectF = newRectF(); 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:画笔。
> * 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轴privatefloat[] pts = {
100, 200, 100, 600,
100, 400, 300, 400,
300, 200, 300, 600}; // 绘制几条线canvas.drawLines(pts, paint);
瞅瞅效果?
绘制指定点,这里直接绘制所有点:
privatefloat[] 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=newRectF(); 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=newRectF(); 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,我麻烦点就麻烦点吧。
感谢大家查阅,不足之处欢迎提出,随时修改,完善~
责任编辑: