自定义view
是区分中级开发和初级开发的分水岭,虽说今年校招,工作三四年的老程序员一直在劝退客户端,作为职场打拼多年的老菜鸟,对android
还是挺有信心的,虽说对view
的知识也只是停留在纸上,很少真正落地做一些复杂高性能的ui控件,之前在akulaku
确实见识了一群技术大牛,高级ui
控件伸手就来,让我羡慕不已,这一次我也从基础到源码再到实战开始写几篇自定义view
教程。大家有什么好的见解也欢迎到评论区多多交流。
一. 自定义UI
的基本方法
说到自定义view
,我们就能联想到最基本的三个方法: onmeasure()
、onlayout()
、ondraw()
;view
能在activity
显示出来。
都要经历测量
、布局
和绘制
三个步骤,而这三个步骤分别对应三个动作:measure
、layout
和draw
。那他们分别代表什么作用呢?
- 测量:
onmeasure()
决定view
的大小; - 布局:
onlayout()
决定view
在viewgroup
中的位置; - 绘制:
ondraw()
决定绘制这个view
。
二. 自定义UI
分类
按照ui
继承属性来说,其实自定义view
大概可分为两种,一种是自定义单view
,一种是自定义viewgroup
,自定义单view
又根据重写方法不同分为,自绘view
和继承view
,他们的区别是:自绘view
仅仅设置特定ui,不会涉及交互,继承view
是基于系统特殊控件,保留原有功能,加以扩展。自定义viewgroup
直接让组合控件成为一个新的父布局控件,这种方式难度有点大。
- 自定义
View
- 只需要重写
onmeasure()
和ondraw()
- 自定义
viewgroup
- 则只需要重写
onmeasure()
和onlayout()
三. 自定义UI
基础
3.1 view
的分类
刚刚说了,视图View
主要分为两类,这边我再次总结一下:
类别 | 解释 | 特点 |
自定义单view |
即一个view ,如textview |
不包含子view |
自定义viewgroup |
即多个view 组成的viewgroup ,如LinearLayout |
包含子view |
3.2 view
类简介
学习自定义view
,我们首先得知道,它是干什么用的,为什么会有这个东西?然后了解他里面是如何实现的?这边先聊聊他为什么会在android
里出现,总结原因有两个:
view
类是android
中各种组件的基类,如view
是viewgroup
基类view
表现为显示在屏幕上的各种视图
android
中的ui
组件都由view
、viewgroup
组成。
点开view
源码,我们发现里面有四个关键的构造函数,下面我就详细讲解一下,每个构造方法所代表的现实意义吧。
源码分析:
// 1. 如果View是在Java代码里面new的,则调用第一个构造函数 public CarsonView(Context context) { super(context); } // 2. 如果View是在.xml里声明的,则调用第二个构造函数 // 自定义属性是从AttributeSet参数传进来的 public CarsonView(Context context, AttributeSet attrs) { super(context, attrs); } // 不会自动调用 // 3. 一般是在第二个构造函数里主动调用 // 如View有style属性时 public CarsonView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } // API21之后才使用 // 不会自动调用 // 4. 一般是在第二个构造函数里主动调用 // 如View有style属性时 public CarsonView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); }
3.3 attributeset
与自定义属性
系统自带的View
可以在xml
中配置属性,对于写的好的自定义view
同样可以在xml
中配置属性,为了使自定义的view
的属性可以在xml
中配置,需要以下4个步骤:
- 通过
为自定义
view
添加属性 - 在
xml
中为相应的属性声明属性值 - 在运行时(一般为构造函数)获取属性值
- 将获取到的属性值应用到
view
3.4 view
视图结构
对于多view
的视图,结构是树形结构:最顶层是viewgroup
,viewgroup
下可能有多个viewgroup
或view
,如下图:
phonewindow
是android
系统中最基本的窗口系统,继承自windows
类,负责管理界面显示以及事件响应。它是activity
与view
系统交互的接口decorview
是phonewindow
中的起始节点view
,继承于view
类,作为整个视图容器来使用。用于设置窗口属性。它本质上是一个framelayout
viewroot
在activtiy
启动时创建,负责管理
、布局
、渲染窗口UI
等等
一定要记住: 无论是measure
过程、layout
过程还是draw
过程,永远都是从view
树的根节点开始测量或计算(即从树的顶端开始),一层一层、一个分支一个分支地进行(即树形递归),最终计算整个view
树中各个view
,最终确定整个view
树的相关属性。
3.5 android
坐标系
android
的坐标系定义为:
- 屏幕的左上角为坐标原点
- 向右为x轴增大方向
- 向下为y轴增大方向
- 、
- ❌ 注意事项: 不要把他和的数学坐标系搞混了
3.6 view
位置(坐标)描述
view
的位置由「4个顶点」决定的「4个顶点的位置」描述分别由4个值决定:
请记住:
view
的位置是相对于父控件而言的
- Top:子
view
上边界到父view
上边界的距离 - Left:子
view
左边界到父view
左边界的距离 - Bottom:子
view
下边距到父view
上边界的距离 - Right:子
view
右边界到父view
左边界的距离
3.7 view
位置获取方式
view
的位置是通过view.getxxx()
函数进行获取:(以Top
为例
// 获取Top位置 public final int getTop() { return mTop; } // 其余如下: getLeft(); // 获取子View左上角距父View左侧的距离 getBottom(); // 获取子View右下角距父View顶部的距离 getRight(); // 获取子View右下角距父View左侧的距离
与MotionEvent
中 get()
和getRaw()
的区别
//get() :触摸点相对于其所在组件坐标系的坐标 event.getX(); event.getY(); //getRaw() :触摸点相对于屏幕默认坐标系的坐标 event.getRawX(); event.getRawY();
3.8 android
中颜色相关内容
3.8.1 Android支持的颜色模式:
3.8.2 以ARGB8888为例介绍颜色定义: