涉及知识
绘制过程
类别 | API | 描述 |
布局 | onMeasure | 测量View与Child View的大小 |
onLayout | 确定Child View的位置 | |
onSizeChanged | 确定View的大小 | |
绘制 | onDraw | 实际绘制View的内容 |
事件处理 | onTouchEvent | 处理屏幕触摸事件 |
重绘 | invalidate | 调用onDraw方法,重绘View中变化的部分 |
Canvas涉及方法
类别 | API | 描述 |
绘制图形 | drawPoint, drawPoints, drawLine, drawLines, drawRect, drawRoundRect, drawOval, drawCircle, drawArc | 依次为绘制点、直线、矩形、圆角矩形、椭圆、圆、扇形 |
绘制文本 | drawText, drawPosText, drawTextOnPath | 依次为绘制文字、指定每个字符位置绘制文字、根据路径绘制文字 |
画布变换 | translate, scale, rotate, skew | 依次为平移、缩放、旋转、倾斜(错切) |
画布裁剪 | clipPath, clipRect, clipRegion | 依次为按路径、按矩形、按区域对画布进行裁剪 |
Paint涉及方法
类别 | API | 描述 |
颜色 | setColor,setARGB,setAlpha | 依次为设置画笔颜色、透明度 |
类型 | setStyle | 填充(FILL),描边(STROKE),填充加描边(FILL_AND_STROKE) |
抗锯齿 | setAntiAlias | 画笔是否抗锯齿 |
字体大小 | setTextSize | 设置字体大小 |
字体测量 | getFontMetrics(),getFontMetricsInt() | 返回字体的测量,返回值一次为float、int |
文字宽度 | measureText | 返回文字的宽度 |
文字对齐方式 | setTextAlign | 左对齐(LEFT),居中对齐(CENTER),右对齐(RIGHT) |
宽度 | setStrokeWidth | 设置画笔宽度 |
笔锋 | setStrokeCap | 默认(BUTT),半圆形(ROUND),方形(SQUARE) |
(PS: 因API较多,只列出了涉及的方法,想了解更多,请查看官方文档)
一、坐标系
1、屏幕坐标系
屏幕坐标系以手机屏幕的左上角为坐标原点,过的原点水平直线为X轴,向右为正方向;过原点的垂线为Y轴,向下为正方向。
2、View坐标系
View坐标系以父视图的左上角为坐标原点,过的原点水平直线为X轴,向右为正方向;过原点的垂线为Y轴,向下为正方向。
View内部拥有四个函数,用于获取View的位置
[代码]java代码:
1 2 3 4 |
|
二、绘制过程
1、构造函数
构造函数用于读取一些参数、属性对View进行初始化操作
View的构造函数有四种重载方法,分别如下:
[代码]java代码:
1 2 3 4 |
|
context:上下文,新建时传入,如:
[代码]java代码:
1 |
|
AttributeSet:是节点的属性集合,如:
[代码]xml代码:
1 2 3 4 5 |
|
即com.customview.BaseChart节点中的属性集合
defStyleAttr:默认风格,是指它在当前Application或Activity所用的Theme中的默认Style,如:
在attrs.xml中添加
[代码]xml代码:
1 |
|
引用的是styles.xml文件中
[代码]xml代码:
1 2 3 4 |
|
在当前默认主题中添加这个style
[代码]xml代码:
1 2 3 4 5 |
|
defStyleRes:默认风格,只有当defStyleAttr无效时,才会使用这个值,如:
在style.xml中添加
[代码]xml代码:
1 2 3 4 |
|
一个实例——BaseChart
新建BaseChart类机成自view
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
|
obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)新增加的attrs属性说明如下:
attrs:默认属性,告诉系统需要获取那些属性的值,有多种Value类型,这里使用string类型,如:
在attrs.xml中添加
[代码]xml代码:
1 2 3 4 5 6 7 |
|
使用上面提到的变量属性和布局文件
a、defStyleAttr与defStyleRes参数先设置为0
运行后显示如下:
[代码]xml代码:
1 2 3 4 5 |
|
attr1与attr2输出均来自布局文件的设置
b、修改BaseView.java设置,引入defStyleAttr:
[代码]java代码:
1 |
|
相当于在布局文件中设置:
[代码]java代码:
1 |
|
运行后显示如下:
[代码]java代码:
1 2 3 4 5 |
|
attr1:仅在布局文件中设置,所以输出为 attr1 from xml
attr2:在布局文件与默认主题的base_chart_style都进行了设置,布局文件中的设置优先级更高,所以输出为 attr2 from xml
attr3:仅在默认主题base_chart_style中进行了设置,所以输出为 attr3 from BaseChartStyle
c、在布局文件中增加自定义的style
运行后结果如下:
[代码]xml代码:
1 2 3 4 5 |
|
attr1:仅在布局文件中设置,所以输出为 attr1 from xml
attr2:在布局文件与默认主题的base_chart_style都进行了设置,布局文件中的设置优先级更高,所以输出为 attr2 from xml
attr3:在默认主题base_chart_style与自定义主题的xml_style都进行了设置,自定义主题优先级更高,所以输出为 attr3 from xml_style
attr4:仅在自定义主题xml_style中进行了设置,所以输出为 attr4 from xml_style
d、修改BaseView.java设置,引入defStyleRes,修改defStyleAttr为0,否则引入的R.style.base_chart_res不会生效:
[代码]java代码:
1 |
|
运行后输入结果如下:
[代码]xml代码:
1 2 3 4 5 |
|
attr1:仅在布局文件中设置,所以输出为 attr1 from xml
attr2:仅在布局文件中进行了设置,所以输出为 attr2 from xml
attr3:仅在自定义主题xml_style中进行了设置,所以输出为 attr3 from xml_style
attr4:在自定义主题xml_style和defStyleRes中都进行了设置,自定义主题优先级更高,所以输出为 attr4 from xml_style
attr5:仅在defStyleRes中进行了设置,所以输出为 attr5 from base_chart_res
2、onMeasure
View会在此函数中完成自己的Measure以及递归的遍历完成Child View的Measure,某些情况下需要多次Measure才能确定View的大小。
可以从onMeasure中取出宽高及其他属性:
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 |
|
由此可见widthMeasureSpec, heightMeasureSpec并不仅仅是宽高的值,还对应了宽高的测量模式。
MeasureSpec是View内部的一个静态类,下面给出它的部分源码:
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
可以看出MeasureSpec代表一个32的int值,高2位代表测量模式SpecMode,低30位代表测量值SpecSize。拥有3种测量模式,分别为UNSPECIFIED、EXACTLY、AT_MOST。
测量类型 | 对应数值 | 描述 |
UNSPECIFIED | 0 | 父容器不对 view 有任何限制,要多大给多大 |
EXACTLY | 1 | 父容器已经检测出 view 所需要的大小,比如固定大小xxdp |
AT_MOST | 2 | 父容器指定了一个大小, view 的大小不能大于这个值 |
3、onLayout
用于确定View以及其子View的布局位置,在ViewGroup中,当位置被确定后,它在onLayout中会遍历所有的child并调用其layout,然后layout内部会再调用child的onLayout确定child View的布局位置。
layout方法如下:
[代码]java代码:
1 2 3 4 5 6 7 8 9 |
|
mLeft, mTop, mBottom, mRight四个参数分别通过getLeft(),getTop(),getRight(),getBottom()四个函数获得。这一组old值会在位置改变时,调用onLayoutChange时使用到。
4、onSizeChanged
如其名,在View大小改变时调用此函数,用于确定View的大小。至于View大小为什么会改变,因为View的大小不仅由本身确定,同时还受父View的影响。
[代码]java代码:
1 2 3 4 |
|
这里的w、h就是确定后的宽高值,如果查看View中的onLayoutChange也会看到类似的情况,拥有l, t, r, b, oldL, oldT, oldR, oldB,新旧两组参数。
5、onDraw
onDraw是View的绘制部分,给了我们一张空白的画布,使用Canvas进行绘制。也是后面几篇文章所要分享的内容。
[代码]java代码:
1 2 3 4 |
|
6、其他方法以及监听回调
如onTouchEvent、invalidate、requestLayout、setOnTouchListener等方法。
onTouchEvent用于处理传递到的View手势事件。
[代码]java代码:
1 2 3 4 |
|
当返回true时,说明该View消耗了触摸事件,后续的触摸事件也由它来进行处理。返回false时,说明该View对触摸事件不感兴趣,事件继续传递下去。
触屏事件类型被封装在MotionEvent中,MotionEvent提供了很多类型的事件,主要关心如下几种类型:
事件类型 | 描述 |
ACTION_DOWN | 手指按下 |
ACTION_MOVE | 手指移动 |
ACTION_UP | 手指抬起 |
事件效果如下:
在MotionEvent中有两组可以获得触摸位置的函数
[代码]java代码:
1 2 3 4 |
|
onWindowFocusChanged运行于onMeasure与onLayout之后,可以获取到正确的width、height、top、left等属性值。
三、小结
简单分析了自定义View的入门准备知识,包括屏幕坐标系、View坐标、View的绘制过程中的主要函数、以及屏幕触摸事件。后面的内容将会围绕onDraw函数展开,在完成涉及知识点的分析之后,将会实战去编写PieView的代码。
共同学习,写下你的评论
评论加载中...
作者其他优质文章