我写文章基本都会先把定义理解清楚,再去写代码和分析问题。比如说这次要分享的是绘制流程,就其控件类型而言分为View(无子View)和ViewGroup。那么正式开始吧:
何为绘制流程Android,用官方一点的话说就是测量(Measure)/布局(Layout)/绘制(Draw)的统称。用自己的话说就是把需要的界面显示在设备上即可。Activity作为
Android的一个载体存在,那么首先要知道Activity在Android中的UI层级关系:
Activity---->PhoneWindow---->DecorView---->Title View和Content View
1.先说Content View 就是我们在Activity调用setContentView方法的布局,所以方法叫setContentView是不是很好理解了。
2.PhoneWindow 是Activity最基本的窗口系统,每个Activity会创建一个。PhoneWindow是Android 和Activity的交互接口,DecorView是Activity的最顶层。(不理解也没有关系)
到现在为止为一小节,简单介绍了Android 整体绘制的一个轮廓。
下面开始从ViewRootImpl根视图的 performTraversals()方法开始说起,为什么是这个方法,那就要从启动Activity时的源码分析,我还没有彻底弄懂Activity的启动流程。就不扯远了。
在PerformTraversals中 分别让顺序调用了PerformMeasure/PerformLayout/PerformDraw方法。接下来分析者三个方法都做了什么。
performMeasure(1,2):
performMeasure是测量控件具体宽高(并不一定是实际宽高)
(在讲Measure测量的时候有个类是必须要弄懂的MeasureSpec,有关MeasureSpec重新写了一篇文章,就好比一个方法只做一件事。有关MeasureSpec类的介绍请看XXX(还没写,哈哈))
现在我们理解了MeasureSpec类,接下来看PerforemMeasure里有什么吧。其实就是调用了mView.measure(1,2) (1/2代表参数),此后不再介绍。然后看View类的measure(1,2)方法吧!
然而View的measure方法只是正对自身View的,所以我们要去ViewGroup开始找有关measure方法。我们发现ViewGroup方法里有个measureChildren(1,2)方法,会根据children(子View)的个数循环调用measureChild(1,2)方法,重新测量后(根据父的MeasureSpec和自身LayoutParams信息得出),又调用了View.measure方法。measure方法调用了自身的onMeasure方法,(这个就是我们自定义veiw重写onMeasure的方法),如果没有重写就会调用getDefaultSize来获取View 的宽高。这样在ViewGroup的measureChildren的for循环下就测量完所有View的宽高了。走势图来一波:
performMeasure—>veiw.measure(1,2)---->onMeasure(1,2)---->getDefaultSize(1.2)(默认)
ViewGroup—>measureChildren(1,2)–循环–>measureChild(1,2)—>veiw.measure(1,2)…
PS在整个measure期间,都有关于MeasureSpec测量的使用,所以要弄懂Measure(测量)首先要掌握MeasureSpec的用法!!!这里只是梳理了整个测量的流程,具体的测量并没有说明。(后续添加吧)
performLayout:
performLayout是用来确定View在父容器的布局位置,父容器获取子View的测量宽高参数(getMeasuredWidth/getMeasuredHeight),然后调用了View的layout方法,然后走了View的OnLayout方法,onLayout在View或者ViewGroup都是个空实现。在layout方法中确定自身的位置,而OnLayout是确定所有子View的位置。具体OnLayout的实现就要更具布局特性自己去实现,比如LinearLayout类中OnLayout就是根据横向/纵向分别确定布局位置。大概的思路就是:
ViewGroup.Onlayout()—循环---->View.layout方法.(确定自身布局的位置,传过来的参数就是measure测量的宽高)。
把握整体思路后,然后重写onLayout方法时就可以去参考已有控件的实现逻辑了。
performDraw:
最后就是ViewRootImpl中的performDraw方法,执行了ViewRootImpl.draw方法。
需要记住View.draw绘制到屏幕的具体步骤:
1.绘制背景 background.draw(canvas)
2.绘制自己(OnDraw)
3.绘制Children (dispatchDraw) —>ViewGroup
4.绘制装饰(onDrawScrollBars)
其中的精髓在于dispatchDraw方法,顶层是个View,执行到draw方法,然后到disparchDraw方法,disparchDraw遍历调用了View的draw方法,然后又执行了四大步来反复绘制所有View。最终经过measure测量和layout布局位置到draw绘制到屏幕上。
有没有发现一个规律:都是在ViewGroup中循环执行View中的方法(自己总结时发现的,就是所谓的指导思想吧)