android View绘制流程与机制

要想知道android绘制的整体流程,我们需要先知道android的UI管理系统的层次关系。

android的UI管理系统的层次关系

PhoneWindow是android系统中最基本的窗口系统,每个Activity会创建一个,它是Activity和View系统交互的接口。下面的是DectorView,他的本质是一个FrameLayout,是Acitivity中所有的view的祖先,里面依此为ViewGroup,View。

绘制的整体流程

当一个应用启动时,会启动一个MainActivity,android系统会根据activity的布局来对它进行绘制,绘制是从根视图ViewRootperformTraversals()方法开始的,从上到下遍历整个视图树,每个view控件负责绘制自己,而viewGroup负责通知自己的view进行绘制操作。视图绘制的主要操作包括Measure,Layout,Draw。performTraversals()包括了performMeasure(),performLayout(),performDraw()三个主要方法。

Measure

在performMeasure中分发操作是先给viewGroup的,由他在他的OnMeasure()方法中传递给子view,ViewGroup通过遍历自身所有的view,并逐个调用view的onMeasure()实现的。
view的测量是在onMesure()中的,系统提供了一个MeasureSpec类,通过这个类来测量view。测量的模式主要有三种,分别是EXACTLY,AT_MOST,UNSPECIFIED

view的onMesure()默认只能支持EXACTLY模式,如果在对view控件指定宽高值的时候,或者是match_parent属性,就是EXACTLY模式,如果让自定义view支持wrap_content属性,那么就必须重写onMesure()。

重写onMesure()方法的代码如下:

private int measureWidth(int measureSpec){
int result=0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);

if(specMode == MeasureSpec.EXACTLY){
result=specSize;
}
else{
result=200;
if(SpecMode == MeasureSpec.AT_MOST){
result=Math.min(result,specSize);
}
}
return result;

}


android View绘制流程与机制_第1张图片

通过上面分析可以看出measure过程主要就是从顶层父View向子View递归调用view.measure方法(measure中又回调onMeasure方法)的过程。具体measure核心主要有如下几点:
MeasureSpec(View的内部类)测量规格为int型,值由高2位规格模式specMode和低30位具体尺寸specSize组成。其中specMode只有三种值:
MeasureSpec.EXACTLY //确定模式,父View希望子View的大小是确定的,由specSize决定;
MeasureSpec.AT_MOST //最多模式,父View希望子View的大小最多是specSize指定的值;
MeasureSpec.UNSPECIFIED //未指定模式,父View完全依据子View的设计值来决定;
View的measure方法是final的,不允许重载,View子类只能重载onMeasure来完成自己的测量逻辑。
最顶层DecorView测量时的MeasureSpec是由ViewRootImpl中getRootMeasureSpec方法确定的(LayoutParams宽高参数均为MATCH_PARENT,specMode是EXACTLY,specSize为物理屏幕大小)。
ViewGroup类提供了measureChild,measureChild和measureChildWithMargins方法,简化了父子View的尺寸计算。
只要是ViewGroup的子类就必须要求LayoutParams继承子MarginLayoutParams,否则无法使用layout_margin参数。
View的布局大小由父View和子View共同决定。
使用View的getMeasuredWidth()和getMeasuredHeight()方法来获取View测量的宽高,必须保证这两个方法在onMeasure流程之后被调用才能返回有效值。

Layout

layout过程用来确定view在父容器中的布局位置,他是由父容器获取子View的位置参数后,调用子view的Onlayout方法并将位置参数传入实现的。

android View绘制流程与机制_第2张图片

从上面分析可以看出layout也是从顶层父View向子View的递归调用view.layout方法的过程,即父View根据上一步measure子View所得到的布局大小和布局参数,将子View放在合适的位置上。具体layout核心主要有以下几点:
1、View.layout方法可被重载ViewGroup.layout为final的不可重载ViewGroup.onLayout为abstract的,子类必须重载实现自己的位置逻辑
2、measure操作完成后得到的是对每个View经测量过的measuredWidth和measuredHeight,layout操作完成之后得到的是对每个View进行位置分配后的mLeft、mTop、mRight、mBottom,这些值都是相对于父View来说的。
3、凡是layout_XXX的布局属性基本都针对的是包含子View的ViewGroup的,当对一个没有父容器的View设置相关layout_XXX属性是没有任何意义的
4、使用View的getWidth()和getHeight()方法来获取View测量的宽高,必须保证这两个方法在onLayout流程之后被调用才能返回有效值。

Draw

先来看下View树的递归draw流程图,如下:
android View绘制流程与机制_第3张图片

第一步,对View的背景进行绘制。draw();
第二步,对View的内容进行绘制。(空方法)onDraw();
第三步,对当前View的所有子View进行绘制,如果当前的View没有子View就不需要进行绘制。(空方法)也就是说只有viewGroup才有此方法的实现。dispatchDraw();
第四步,对View的滚动条进行绘制。其实任何一个View都是有(水平垂直)滚动条的,只是一般情况下没让它显示而已。onDrawScrollBars();
可以看见,绘制过程就是把View对象绘制到屏幕上,整个draw过程需要注意如下细节:
1、如果该View是一个ViewGroup,则需要递归绘制其所包含的所有子View。
2、View默认不会绘制任何内容,真正的绘制都需要自己在子类中实现。
3、View的绘制是借助onDraw方法传入的Canvas类来进行的。
4、区分View动画和ViewGroup布局动画,前者指的是View自身的动画,可以通过setAnimation添加后者是专门针对ViewGroup显示内部子视图时设置的动画可以在xml布局文件中对ViewGroup设置layoutAnimation属性(譬如对LinearLayout设置子View在显示时出现逐行、随机、下等显示等不同动画效果)。
在获取画布剪切区(每个View的draw中传入的Canvas)时会自动处理掉padding5、View获取Canvas不用关注这些逻辑,只用关心如何绘制即可。
6、默认情况下子View的ViewGroup.drawChild绘制顺序和子View被添加的顺序一致,但是你也可以重载ViewGroup.getChildDrawingOrder()方法提供不同顺序。

本文参考了【工匠若水 http://blog.csdn.net/yanbober 】的【Android应用层View绘制流程与源码分析】,并写下了自己的一些理解。

你可能感兴趣的:(android原理与机制)