前言
作为一个Android手机用户,最直观明显感受一个app的好坏的标准就是UI效果,一个好的UI体验是提升用户粘性先决条件。所以最为Android的开发者,学习自定义View是我们绕不开也躲不掉的。如何开发一个稳定、健壮的自定义View呢?这需要我们对自定义View流程及原理的理解程度的多少所决定。
View类简介
- View类是Android中各种视图View的基类,如View 是ViewGroup的基类
- View的表现为显示在屏幕上的各种视图
View的构造函数:共有4个构造函数
View构造函数
// 如果View是在Java代码里面new的,则调用第一个构造函数
public TestView(Context context) {
super(context);
}// 如果View是在.xml里声明的,则调用第二个构造函数
// 自定义属性是从AtttrbuteSet参数传进来的
public TestView(Context context, AttributeSet attrs) {
super(context, attrs);
}
// 不会自动调用,一般是在第二个构造函数里面主动调用,如果view 有style属性时
public TestView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}// API 21 之后才使用
// 不会自动调用,一般是在第二个构造函数里面主动调用,如果view 有style属性时
public TestView(Context context, AttributeSet attrs, int defStyleAttr
, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}复制代码
AttributeSet与自定义属性
系统自带的View可以在xml中配置属性,自定义View也同样可以在xml中配置属性,为了使自定义View的属性可以在xml中配置,需要以下4个步骤
- 通过
<declare-styleable>
为自定义View添加属性 - 在xml中为相应的属性声明属性值
- 在运行时(一般在构造函数)获取属性值
- 将获取到的属性值应用到View
View的绘制流程
如图所示,view的各个函数的功能及执行顺序
注意:invalidate()函数能够让视图重新执行onDraw()而不需要重新测量布局, requestLayout()函数能够重新执行onMeasure()->onLayout()->onDraw() 绘制流程。所以在刷新视图的时候如果仅仅只改变了视图内容我们只需要调用invalidate() 或 postInvalidate(),减少没必要的cpu开销从而提升性能
View的分类
自定义View按布局能力区分,可以分为View和ViewGroup
- 自定义View
- 在没有现成的View,需要自己实现的时候,就使用自定义View,一般继承自View,SurfaceView或其他的View
- 主要是实现 onMeasure + onDraw
- 自定义ViewGroup
- 自定义ViewGroup一般是利用现有的组件根据特定的布局方式来组成新的组件,大多继承自ViewGroup或各种Layout
- 主要是实现onMeasure + onLayout
View视图结构
- PhoneWindow是Android系统中最基本的窗口系统,继承自Windows类,负责管理界面显示以及事件响应。它是Activity与View系统交互的接口
- DecorView是PhoneWindow中的起始点View,继承于View类,作为整个视图容器来使用。用于设置窗口属性。它本质上是一个FrameLayout
- ViewRoot在Activity启动时创建,负责管理、布局、渲染窗口UI等等
对于多View视图,结构是树形结构:如下
注意:无论是measure过程,layout过程还是draw过程,永远都是从View树的根节点开始测量或计算,一层层、一个分支一个分支的进行,最终计算整个View树的各个View,最终确定整个View树的相关属性
Android坐标系
自定义View除了上面必须掌握的知识点,还需要了解Android 坐标点。
Android的坐标系定义为:
- 屏幕的左上角为坐标原点
- 向右为X轴增大方向
- 向下为Y轴增大方向
View位置描述:
View的位置由4个顶点决定,4个顶点的描述由4个值决定:
(请记住View的位置是相对父控件而言的)
- Top:子View上边界距离父View上边界的距离
- Left:子View左边界距离父View左边界的距离
- right:子View右边界距离父View右边界的距离
- Bottom:子View下边界距离父View下边界的距离
获取位置的方式:
View的位置通过getXXX()来获取
view.getTop(); // 获取top的位置
//下面同理
view.getLeft();
view.getRight();
view.getBottom();复制代码
于MotionEvent 的getXXX()和getRawXXX()的区别
// 触摸点相对于其所在组件坐标系的坐标
event.getX()
event.getY()
// 触摸点相对于屏幕默认坐标系的坐标
event.getRawX()
event.getRawY()
复制代码