作为一名Android开发者肯定明白View的地位,说它占据半壁江山也不为过,作为基石之一,搞明白它的加载流程是每个开发者都应该去做的,目前网络上很多关于View绘制流程的文章,有些质量也很高,但我还是想以自己的思路出一篇文章。相信读完你对View的工作机制以及自定义View会有一个全新的认识。
Activity
都会创建一个
Window
用于承载View视图的显示,
Window
是一个抽象类存在了一个唯一实现类
PhoneWindow
FrameLayout
子类,最终会被加载到Window当中,它内部只有一个垂直方向的
LinearLayout
分为两部分:
Activity
对应的XML布局,通过
setContentView
设置到
DecorView
中。首先来看一下Activity中setContentView源码:
从代码可以看出,
Activity
的
setContentView
实质是将
View
传递到
Window
的
setContentView()
方法中,
Window
的
setContenView
会在内部调用
installDecor()
方法创建
DecorView
,看一下它的部分源码:
通过
generateDecor()
new一个
DecorView
,然后调用
generateLayout()
获取
DecorView
中
content
,最终通过
inflate
将
Activity
视图添加到
DecorView
中的
content
中,但此时
DecorView
还未被添加到
Window
中。添加操作需要借助
ViewRootImpl
。
ViewRootImpl
的作用是用来衔接
WindowManager
和
DecorView
,在
Activity
被创建后会通过
WindowManager
将
DecorView
添加到
PhoneWindow
中并且创建
ViewRootImpl
实例,随后将
DecorView
与
ViewRootImpl
进行关联,最终通过执行
ViewRootImpl
的
performTraversals()
开启整个View树的绘制。
关于Activity在何时将DecorView添加到Window以及何时创建 ViewRootImpl,这块内容牵扯面比较广,涉及到Activity启动流程、ActivityManagerService(AMS)、WindowManagerService(WMS),内容太过于深入加上作者能力有限就不误人子弟了。如有兴趣推荐查阅刘皇叔《Android进阶解密》,书中对这方面内容讲解还是比较全面的 。
从第一小节可知,View的绘制是从
ViewRootImpl
的
performTraversals()
方法开始,从最顶层的
View(ViewGroup)
开始逐层对每个
View
进行绘制操作,下面来看一下该方法部分源代码:
这方法大概有几百行,机智的作者抽出三句精华呈现给大家~~~
- measure:为测量宽高过程,如果是ViewGroup还要在onMeasure中对所有子View进行measure操作。
- layout:用于摆放View在ViewGroup中的位置,如果是ViewGroup要在onLayout方法中对所有子View进行layout操作。
- draw:往View上绘制图像。
示意图如下:
确实不想画图了,从刚哥的书里拍一张吧~~~
performMeasure()
源码
可以看出从mView(最顶层ViewGroup)开始进行测量操作,然后逐层遍历View并执行measure操作。
Measure
是
View
绘制三个过程中的第一步,提到
Measure
就不得不提
MeasureSpac
它是一个32位
int
类型数值,高两位
SpacMode
代表测量模式,低30位
SpacSize
代表测量尺寸,是View的内部类,源码如下:
内部也包含三种测量模式:
ScrollView
就是这种测量模式。match_parent
或者xxxdp,表示父布局已经决定了子
View
的大小,通常在这种情况下
View
的尺寸就是
SpacSize
wrap_content
子View可以根据内容设置自己的大小,但前提是不能超出父
ViewGroup
的宽高。在我们自定义View的过程中都会在onMeasure中进行宽高的测量,这个方法会从父布局中接收两个参数
widthMeasureSpac
和heightMeasureSpac
,所以子布局的宽高大小需要受限于父布局。
在自定义View宽高测量的过程中,我们需要获取
MeasurSpac
中的宽高和测量模式,自定义
ViewGroup
也必须给子View传递
MeasurSpac
,Android也给我们提供了计算
MeasurSpac
和通过
MeasurSpac
获取相应值的方式,都位于
MeasurSpac
中,具体代码如下:
从
ViewGroup
到
View
对尺寸和模式进行了一次封装和拆解,其目的是为了减少对象的创建,避免造成不必要的内存浪费。
在刚接触Android的时候经常有一个疑问,为什么View设置自己的宽高,还要创建一个
xxx.LayoutParams
?前面也提到了,子View的宽高是要受限于父布局的,所以不能通过
setWidth
或者
setHeight
直接设置宽高的,另外
LayoutParams
的作用不仅如此,比如一个View的父布局是
RelativeLayout
,可以通过设置
RelativeLayout.LayoutParams
的
above
,
below
等属性来调整在父布局中的位置。
创建一个类继承View,重写其
onMeasure()
方法
一般的自定义View中,如果对宽高没有特殊需求可直接通过
getDefaultSize()
方法获取,该方法位于View中源码如下:
从代码分析可知,获取
mode
和
size
后会分别对三种测量模式进行判断,
UNSPECIFIED
使用默认尺寸,而
AT_MOST
和
EXACTLY
使用父布局给出的测量尺寸。尺寸计算完毕后通过
setMeasuredDimension(width,height)
设置最终宽高。
performLayout()
部分源码:
跟measure类似,同样是从
mView(最顶层ViewGroup)
开始进行
layout
操作,随后逐层遍历。
layout(l,t,r,b)
四个参数分别对应
左上右下
的位置,从而确定View在ViewGroup中的位置。下面来看一下
layout()
部分源码:
结合源码可知
layout()
会将四个位置参数传递给
setOpticalFrame()
或者
setFrame()
,而
setOpticalFrame()
内部会调用
setFrame()
,所以最终通过
setFrame()
确定
View
在
ViewGroup
中的位置。位置确定完毕会调用
onLayout(l,t,r,b)
对子View进行摆放。
View
和
ViewGroup
在执行完
setFrame()
后都会调用
onLayout()
方法,但上面也有提到该方法的作用是对子View进行位置摆放,所以单一View是不需要重写此方法。而
ViewGroup
会根据自己的特性任意对子View进行摆放。
相信很多学习自定义View的同学都是奔着有朝一日自己也实现那些眼花缭乱的效果,起码我自己就是。我们在手机上看到的那些五彩缤纷的图片,动画都是在这个方法内绘制而成。
相比于measure和layout阶段,draw阶段中View和ViewGroup变得没那么紧密了,View的绘制过程中不需要考虑ViewGroup,而ViewGroup也只需触发子View的绘制方法即可。
performDraw()
执行后同样会从根布局开始逐层对每个View进行draw操作,在View中绘制操作时通过
draw()
进行,来看一下其主要源码:
draw()方法中主要包含四部分内容,其中我们开发者只需要关心onDraw(canvas)即可,即自身的内容绘制。
关于绘制内容这部分可利用到的知识点很多,多到可以写一本书出来,所以仅靠本文全部详细描述显然是不现实的。下面我罗列一部分常用内容供大家参考:
- Canvas:画布,不管是文字,图形,图片都要通过画布绘制而成
- Paint:画笔,可设置颜色,粗细,大小,阴影等等等等,一般配合画布使用
- Path:路径,用于形成一些不规则图形。
- Matrix:矩阵,可实现对画布的几何变换。
文章从四个方面总结了View的绘制流程:
绘制时机
,
宽高测量
,
位置摆放
,
图像绘制
,因为侧重于流程所以只是把这四部分的精华给拎出来分享给大家,起到一个抛砖引玉的作用,想要透彻理解启动流程、玩转自定义View还需要对各部分知识系统的学习。
这里今天给大家分享一份Android进阶学习资料,主要为安卓相关知识点及面试资料为主,在这个PDF中,通过 详解各大互联网公司的 Android 常见面试题为主线,从面试的角度带你介绍必备知识点,以及该知识点在项目中的实际应用。
帮你在现在的基础上,重新梳理和建立 Android 开发的知识体系。无论是你短期内想提升 Android 内功实力,突破自己工作中的能力瓶颈,还是准备参加 Android 面试,都会在这个PDF中有所收获。一些基础不好的,这里也有一份安卓基础资料包,帮助巩固基础。
以下是这份PDF主要内容:
1、确定好方向,梳理成长路线图
不用多说,相信大家都有一个共识:无论什么行业,最牛逼的人肯定是站在金字塔端的人。所以,想做一个牛逼的程序员,那么就要让自己站的更高,成为技术大牛并不是一朝一夕的事情,需要时间的沉淀和技术的积累。
关于这一点,在我当时确立好Android方向时,就已经开始梳理自己的成长路线了,包括技术要怎么系统地去学习,都列得非常详细。
知识梳理完之后,就需要进行查漏补缺,所以针对这些知识点,我手头上也准备了不少的电子书和笔记,这些笔记将各个知识点进行了完美的总结:
2、通过源码来系统性地学习
只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。
真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。
3、阅读前辈的一些技术笔记
4、刷题备战,直通大厂
历时半年,我们整理了这份市面上最全面的安卓面试题解析大全
包含了腾讯、百度、小米、阿里、乐视、美团、58、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。
如何使用它?
1.可以通过目录索引直接翻看需要的知识点,查漏补缺。
2.五角星数表示面试问到的频率,代表重要推荐指数
以上文章中的资料,均可以免费分享给大家来学习,无论你是零基础还是工作多年,现在开始就不会晚。
以上内容均放在了开源项目: 【 github 】 中已收录,大家可以自行获取。
学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以: 贵在坚持!
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/69983917/viewspace-2733351/,如需转载,请注明出处,否则将追究法律责任。