3.6View工作原理
目录介绍
- 01.View的绘制流程
- 02.View工作流程
- 03.View流程框架图
- 04.View绘制原理
- 05.View卡顿原因
01.View的绘制流程
- View的绘制流程主要分为三步:
- onMeasure:测量视图的大小,从顶层父View到子View递归调用measure()方法,measure()调用onMeasure()方法,onMeasure()方法完成绘制工作。
- onLayout:确定视图的位置,从顶层父View到子View递归调用layout()方法,父View将上一步measure()方法得到的子View的布局大小和布局参数,将子View放在合适的位置上。
- onDraw:绘制最终的视图,首先ViewRoot创建一个Canvas对象,然后调用onDraw()方法进行绘制。onDraw()方法的绘制流程为
- 从View的测量、布局和绘制原理来看,要实现自定义View,根据自定义View的种类不同,可能分别要自定义实现不同的方法。但是这些方法不外乎:onMeasure()方法,onLayout()方法,onDraw()方法。
- onMeasure()方法:
- 单一View,一般重写此方法,针对wrap_content情况,规定View默认的大小值,避免于match_parent情况一致。ViewGroup,若不重写,就会执行和单子View中相同逻辑,不会测量子View。一般会重写onMeasure()方法,循环测量子View。
- Measure完成后可以通过getMeasureWidth和getMeasureHeight方法获取到view的测量后的宽高,在几乎所有的情况下都会等于最终view的宽高
- onMeasure()方法接收两个参数,widthMeasureSpec和heightMeasureSpec,这两个值分别用于确定视图的宽度和高度的规格和大小。
- onLayout()方法:
- 单一View,不需要实现该方法。ViewGroup必须实现,该方法是个抽象方法,实现该方法,来对子View进行布局。
- layout 过程决定了View的四个顶点的坐标和实际的View的宽高,完成以后可以通过getTop,getBottom,getLeft,getRight来获取View的四个顶点位置,并通过getWidth,getHeight获取View的最终宽高
- onDraw()方法:
- 无论单一View,或者ViewGroup都需要实现该方法,因其是个空方法
- draw过程则决定了View的显示,完成draw后view会显示在屏幕上
- 绘制背景(background.draw(Canvas))
- 绘制自己 protected void onDraw(Canvas canvas) onDraw绘制自己,新建一个paint 在canvas上绘制自己的图形
- 绘制children (dispatchDraw)dispatchDraw会遍历调用所有子元素的draw方法
- 绘制装饰(onDrawScrollBars)
- 绘制过程
类别 API 描述 测量 onMeasure 测量View与Child View的大小 布局 onLayout 确定Child View的位置 size变化 onSizeChanged 确定View的大小 绘制 onDraw 实际绘制View的内容 事件处理 onTouchEvent 处理屏幕触摸事件 重绘 invalidate 调用onDraw方法,重绘View中变化的部分 重新布局 requestLayout 调用onLayout、onMeasure方法,重新布局
02.View工作流程
- View工作流程
- View工作流程简单来说就是,先measure测量,用于确定View的测量宽高,再 layout布局,用于确定View的最终宽高和四个顶点的位置,最后 draw绘制,用于将View 绘制到屏幕上。
image - ViewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带。
- View的绘制流程是从ViewRoot和performTraversals开始。
- performTraversals()依次调用performMeasure()、performLayout()和performDraw()三个方法,分别完成顶级 View的绘制。
- 其中,performMeasure()会调用measure(),measure()中又调用onMeasure(),实现对其所有子元素的measure过程,这样就完成了一次measure过程;接着子元素会重复父容器的measure过程,如此反复至完成整个View树的遍历。layout和draw同理。
- Measure过程后可以调用getMeasureWidth和getMeasureHeight方法获取View测量后的宽高,与getWidth和getHeight的区别是:getMeasuredHeight()返回的是原始测量高度,与屏幕无关,getHeight()返回的是在屏幕上显示的高度。实际上在当屏幕可以包裹内容的时候,他们的值是相等的,只有当view超出屏幕后,才能看出他们的区别。当超出屏幕后,getMeasuredHeight()等于getHeight()加上屏幕之外没有显示的高度。
- Layout过程确定View四个顶点的位置和实际的宽高。
- Draw过程确定View的显示,只有draw方法完成后View的内容才会出现在屏幕上。
image
- 比较重要的概念
- ViewRoot:连接WindowManager(外界访问Window的入口)和DecorView(顶级View)的纽带,View的三大流程均是通过ViewRoot来完成的。
- DecorView:顶级View
- DecorView是顶级View,本质就是一个FrameLayout
- 包含了两个部分,标题栏和内容栏
- 内容栏id是content,也就是activity中setContentView所设置的部分,最终将布局添加到id为content的FrameLayout中
03.View流程框架图
- 如图所示
img
- View的绘制是从上往下一层层迭代下来的。DecorView-->ViewGroup(--->ViewGroup)-->View ,按照这个流程从上往下,依次measure(测量),layout(布局),draw(绘制)。
img
04.View绘制原理
- View的绘制流程有3个步骤,分别是measure、layout和draw,它们主要运行在系统的应用框架层,而真正将数据渲染到屏幕上的则是系统Native层的SurfaceFlinger服务来完成的。
- 绘制过程主要由CPU来进行Measure、Layout、Record、Execute的数据计算工作,GPU负责栅格化、渲染。CPU和GPU是通过图形驱动层来进行连接的,图形驱动层维护了一个队列,CPU将display list添加到该队列中,这样GPU就可以从这个队列中取出数据进行绘制。
- Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染,如果每次渲染都成功,这样就能够达到流畅的画面所需要的60fps,VSYNC是Vertical Synchronization(垂直同步)的缩写,是一种定时中断,一旦收到VSYNC信号,CPU就开始处理各帧数据。如果某个操作要花费30ms,这样系统在得到VSYNC信号时无法进行正常的渲染,会发生丢帧。
05.View卡顿原因
- 产生卡顿原因有很多,主要有以下几点:
- 布局Layout过于复杂,无法在16ms内完成渲染。
- 同一时间动画执行的次数过多,导致CPU和GPU负载过重。
- View过渡绘制,导致某些像素在同一帧时间内被绘制多次。
- 在UI线程中做了稍微耗时的操作。
- GC回收时暂停时间过长或者频繁的GC产生大量的暂停时间。
06.自定义View优化策略
- 为了加速你的view,对于频繁调用的方法,需要尽量减少不必要的代码。先从onDraw开始,需要特别注意不应该在这里做内存分配的事情,因为它会导致GC,从而导致卡顿。在初始化或者动画间隙期间做分配内存的动作。不要在动画正在执行的时候做内存分配的事情。
- 还需要尽可能的减少onDraw被调用的次数,大多数时候导致onDraw都是因为调用了invalidate().因此请尽量减少调用invaildate()的次数。如果可能的话,尽量调用含有4个参数的invalidate()方法而不是没有参数的invalidate()。没有参数的invalidate会强制重绘整个view。
- 另外一个非常耗时的操作是请求layout。任何时候执行requestLayout(),会使得Android UI系统去遍历整个View的层级来计算出每一个view的大小。如果找到有冲突的值,它会需要重新计算好几次。另外需要尽量保持View的层级是扁平化的,这样对提高效率很有帮助。
- 如果你有一个复杂的UI,你应该考虑写一个自定义的ViewGroup来执行他的layout操作。与内置的view不同,自定义的view可以使得程序仅仅测量这一部分,这避免了遍历整个view的层级结构来计算大小。这个PieChart
- 例子展示了如何继承ViewGroup作为自定义view的一部分。PieChart有子views,但是它从来不测量它们。而是根据他自身的layout法则,直接设置它们的大小。
其他介绍
01.关于博客汇总链接
02.关于我的博客
- github:https://github.com/yangchong211
- 知乎:https://www.zhihu.com/people/yczbj/activities
- 简书:http://www.jianshu.com/u/b7b2c6ed9284
- csdn:http://my.csdn.net/m0_37700275
- 喜马拉雅听书:http://www.ximalaya.com/zhubo/71989305/
- 开源中国:https://my.oschina.net/zbj1618/blog
- 泡在网上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
- 邮箱:yangchong211@163.com
- 阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV
- segmentfault头条:https://segmentfault.com/u/xiangjianyu/articles
- 掘金:https://juejin.im/user/5939433efe88c2006afa0c6e