前言:这是一个关于Android自定义View的学习记录系列,系列中出现的知识点均出自《Android艺术开发探索》,部分摘录自热心网友文章
本文结构:
1. View的基础知识
1.1 View的概念
什么是View,在Android中不管是Button还是TextView,又或者是LinearLayout,它们的共同基类都是View,所以View是界面层的控件的一种抽象,它代表了一种控件。除了View,还有ViewGroup,ViewGroup是控件组,即一组控件(或者说是一组View),它的父类是View,综上,View可以解释为单个控件,也可以解释为由多个控件组件的一组控件。
1.2 View的位置参数
View的位置主要由它的四个属性决定:top、left、right、bottom。如下图坐标系所示:
View的坐标值都是相对于父容器而言的。
这四个属性值的获取方式:
1 | Left = getLeft(); |
而从Android3.0开始,View增加了额外的几个参数:x、y、translationX和translationY,其中x和y是View左上角的坐标,translationX和translationY是View左上角相对于父容器的偏移量,默认值为0。
2.自定义View的原因和方式
2.1 为什么要自定义View
通过自定义View,我们可以实现各种五花八门的效果
2.2 自定义View的方式
- 把系统内置的控件组合起来成一个新的控件
- 继承系统现有的控件,加入新的功能
- 自己绘制控件,继承系统View
3. View的工作流程
View的工作流程主要是指measure、layout和draw这三个流程,即测量、布局和绘制。其中measure确定View的测量宽/高,layout确定View的最终宽/高和四个顶点的位置,而draw则将View绘制到屏幕上。
View的绘制流程是从ViewRoot的performTraversals,它经过measure、layout和draw三个过程才能最终将一个view绘制出来,针对performTraversals的大致流程,如下图所示:
performTraversals会依次调用performMeasure、performLayout和performDraw这三个方法,这三个方法分别完成顶级View的measure、layout和draw这三大流程,其中performMeasure会调用measure方法(measure方法是一个final类型的方法),在measure方法中又会调用onMeasure方法,在onMeasure方法中则会对所有的子元素进行measure过程,这个时候measure流程就从父容器传递到子元素中了,完成了一次measure过程。接着子元素会重复父容器的measure过程,如此反复就完成了整个view树的遍历。同理,performLayout和performDraw的传递流程和performMeasure是类似的。
3.1 measure过程
在上面的分析中我们知道在View的measure方法中会去调用View的onMeasure,因此只需要看onMeasure的实现即可,View的onMeasure方法如下所示:
1 |
|
onMeasure方法里有两个重要的参数, widthMeasureSpec和heightMeasureSpec,它们包含了两个信息:mode和size;
mode(测量模式)代表了我们当前控件的父控件告诉我们控件,你应该按怎样的方式来布局。mode的分类如下表格所示
模式 | 二进制数值 | 描述 |
---|---|---|
UNSPECIFIED | 00 | 父容器不对View有任何限制,要多大给多大 |
EXACTLY | 01 | 父容器已经检测出View所需要的精确大小,这个时候View的最终大小就是SpecSize所指定的值,它对应LayoutParams的match_parent和具体的数值这两种模式 |
AT_MOST | 10 | 父容器指定了一个可用大小SpecSize,View的大小不能大于这个值,它对应LayoutParams的wrap_content |
注意:
如果对View的宽高进行修改了,不要调用 super.onMeasure( widthMeasureSpec, heightMeasureSpec); 要调用 setMeasuredDimension( widthsize, heightsize); 这个函数,否则会报异常。
onSizeChanged(确定View的大小):这个函数在view第一次被指定了大小值或者view的大小发生改变时会被调用。所以一般用来计算一些位置和与view的size有关的值。
3.2 layout过程
layout方法的大致流程如下:首先会通过setFrame方法来设定View的四个顶点的位置,即初始化mLeft、mRight、mTop和mBottom这四个值,View的四个顶点一旦被确定,那么View在父容器中的位置也就被确定了;接着会调用onLayout方法,onLayout的具体实现同样和具体的布局有关。
3.3 draw过程
draw过程比较简单,它的作用是将View绘制到屏幕上面。View的绘制过程遵循:
- 绘制背景background.draw(canvas)
- 绘制自己(onDraw)
- 绘制children(dispatchDraw)
- 绘制装饰(onDrawScrollBars)
4. 自定义View的官方套路
步骤 | 关键字 | 作用 |
---|---|---|
1 | 构造函数 | 初始化和定义自定义属性 |
2 | onMeasure | 确定view的测量宽/高 |
3 | onSizeChanged | 确定view的大小 |
4 | onLayout | 确定view在父容器中的布局 |
5 | onDraw | 实际绘制内容 |
6 | 提供接口 | 控制View或监听View某些状态 |
参考资料:
《Android艺术开发探索》