内容简介:掌握一门技术当然需要知道它的作用,不然学习起来就没有了动力。今天的主题是自定义View,既然谷歌已经提供了那么多得控件,那么为什么还要去自定义View呢?我总结一下主要有以下三大点:这种情况很普遍,比如下拉刷新吧,官方只是提供了一个默认的转圈圈,奇丑无比,当然入不了设计师的眼了,设计师将设计书一改,自定义View的需求就来了。虽然有很多的第三方大牛已经做好了各种各样的效果,但是我们也要了解其中的实现方式,万一效果有差池才能修改。
掌握一门技术当然需要知道它的作用,不然学习起来就没有了动力。今天的主题是自定义View,既然谷歌已经提供了那么多得控件,那么为什么还要去自定义View呢?我总结一下主要有以下三大点:
1.有些UI效果是官方控件没有提供的
这种情况很普遍,比如下拉刷新吧,官方只是提供了一个默认的转圈圈,奇丑无比,当然入不了设计师的眼了,设计师将设计书一改,自定义View的需求就来了。虽然有很多的第三方大牛已经做好了各种各样的效果,但是我们也要了解其中的实现方式,万一效果有差池才能修改。
2.为了提升页面性能
多层的View嵌套是很影响渲染性能的,因为会增加测量的次数
3.出于代码规范性的考虑
将一些常用可复用的效果抽离成自定义View,方便进行复用和统一维护
自定义View的核心方法
自定义View的最基本的三个方法分别是: onMeasure()、onLayout()、onDraw();View在Activity中显示出来,要经历测量、布局和绘制三个步骤,分别对应三个动作:measure、layout和draw。其中自定义View:只需要重写onMeasure()和onDraw(),自定义ViewGroup则只需要重写onMeasure()和onLayout()
-
测量:onMeasure()决定View的大小;
-
布局:onLayout()决定View在ViewGroup中的位置;
-
绘制:onDraw()决定绘制这个View。
AttributeSet与自定义属性
系统自带的View可以在xml中配置属性,对于写的好的自定义View同样可以在xml中配置属性,为了使自定义的View的属性可以在xml中配置,需要以下4个步骤:通过
为自定义View添加属性在xml中为相应的属性声明属性值在运行时(一般为构造函数)获取属性值将获取到的属性值应用到View
View视图结构
PhoneWindow是Android系统中最基本的窗口系统,继承自Windows类,负责管理界面显示以及事件响应。它是Activity与View系统交互的接口DecorView是PhoneWindow中的起始节点View,继承于View类,作为整个视图容器来使用。用于设置窗口属性。它本质上是一个FrameLayoutViewRoot在Activtiy启动时创建,负责管理、布局、渲染窗口UI等等
对于多View的视图,结构是树形结构:最顶层是ViewGroup,ViewGroup下可能有多个ViewGroup或View,如下图:
注意:无论是measure过程、layout过程还是draw过程,永远都是从View树的根节点开始测量或计算(即从树的顶端开始),一层一层、一个分支一个分支地进行(即树形递归),最终计算整个View树中各个View,最终确定整个View树的相关属性。为什么不从子View开始计算呢,因为子View个数太多,处理的顺序不好弄,而且子View若为wrap_content或者match_parent的情况下,就会出现不知道onMeasure时该View应该显示多大了。
Android坐标系
Android的坐标系定义为:
-
屏幕的左上角为坐标原点
-
向右为x轴增大方向
-
向下为y轴增大方向
getY代表触摸点到控件上边缘的距离。getRawY上图有点问题,将就着看看,getRawY应该是从X轴到触摸点的距离,也就是getRawY那条白线还要再往上面延伸一些。
View树的绘制流程
View树的绘制流程是谁负责的?
view树的绘制流程是通过ViewRootImpl去负责绘制的,它的主要作用是View树的管理者,负责将DecorView和PhoneWindow“组合”起来,而View树的根节点严格意义上来说只有DecorView;每个DecorView都有一个ViewRoot与之关联,这种关联关系是由WindowManager去进行管理的;
从上图可以看到,从添加View以后就一直在ViewRootImpl中做处理,最终是执行到了performTraversals方法,这个方式非常核心,它负责调用onMeasure、onLayout、onDraw这几个核心方法,是一条主线。
那么Android系统为什么要有onMeasure呢?
因为Android支持非精确的大小,具体包括warp_content和match_parent,所以在显示的时候并不能只能确定出自己的实际大小,而是需要根据父控件给出的限制以及自身的属性设置来决定,所以我们需要复写onMeasure来设定自己想要的大小策略。
说到父控件给出的限制,这里想要重点讲一下,其实就是MeasureSpec,在复写onMeasure的时候系统给我们回调的参数。其中包括widthMeasureSpec和heightMeasureSpec,也就是父控件分别给的宽高限制,限制包括父容器给当前控件的测量模式,以及父容器具体的大小(当然只有确定了的时候才能给)
在父容器给的MeasureSpec中,总共有32位,最高的2位存储着父容器的测量模式,其余的低30位存储着父容器具体的大小,子View的MeasureSpec值是根据子View的布局参数(LayoutParams)和父容器的MeasureSpec值计算得来的,具体计算逻辑封装在getChildMeasureSpec()里
在子View复写的onMeasure中,需要根据父容器给的MeasureSpec以及该子View使用时设置的宽高属性来得出该View的最终宽高。这里最好能覆盖自定能设置的所有的情况,假设只能设置固定宽高,就不能算是一个合格的自定义View了
系统得出该View的具体策略如下图所示,横向代表父容器给予的测量模式,纵向代表字View自身设置的宽高属性,其中Unspecified代表不知道自己应该有多大,也就是说父容器也是设置的wrap_content的情况,这种情况优先子View确定宽高,如果子View是精确值,则直接确定为具体宽高,否则也不确定
上面说的可能有些抽象,接下来就以自定义ViewGroup为例,说明一下测量过程。在自定义ViewGroup时,具体流程如下:
1.测量ViewGroup自身的尺寸
需要调用super.onMeasure来测量自身的尺寸
2.为每个子View计算测量的限制信息
获取到第一步测量好的信息,包括测量模式、测量出来的数据
3.将上一步获得到的限制信息传递给该ViewGroup的每一个子View,好让子View能做自己的onMeasure测量
4.获取子View测量完成后的尺寸
5.计算ViewGroup自身的尺寸
由于当前ViewGroup的尺寸和子View有关,所以必须等子View的尺寸获取到以后再决定自身的尺寸,这里使用switch是考虑到当前ViewGroup的xml中定义的widh和height有三种情况
6.保存ViewGroup自身的尺寸作为本次onMeasure结果,本次测量结束
onLayout方法
onMeasure方法确定了当前ViewGroup的大小后,onlayout方法就简单了,按照自己的想法调用每个子View的layout方法决定好子View的位置即可,注意位置是相对当前控件的相对位置
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Android 自定义View基础(一)
- Azure 入门基础:自定义 Table storage 查询条件
- 重新定义堡垒机 构建IT基础设施特权身份银行
- 从0到1Android自定义View(一)零散基础知识
- Spring Boot 基础系列:实现一个自定义配置加载器(应用篇)
- Spring Boot 基础系列:实现一个自定义配置加载器(应用篇)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Practical Vim, Second Edition
Drew Neil / The Pragmatic Bookshelf / 2015-10-31 / USD 29.00
Vim is a fast and efficient text editor that will make you a faster and more efficient developer. It’s available on almost every OS, and if you master the techniques in this book, you’ll never need an......一起来看看 《Practical Vim, Second Edition》 这本书的介绍吧!