上一篇文章从ActivityTHread.main()讲到了ViewRootImpl.performTraversals(),这篇则进入performTraversals()的执行流程,也就是大家所熟知的Measure、Layout、Draw三大流程。
关于三大流程讲解的相关文章太多太多了,之所以自己想写一篇,是因为看别人的文章总是磕磕绊绊,他们的讲解方法、重点各不相同,所以总是需要自己参考多篇文章,再加以自己的梳理才能有个相对完整的理解,这就是我也想写一篇的原因,写一篇一气呵成、脉络清晰的讲解出来。
可是后来在查找相关博客时,发现了一篇,无论是在讲解思路、讲解形式、讲解重点上,都是我心中想要完成的文章。文章写的非常细致,讲解特别到位,很适合没有接触过这三大流程的同学学习,相信看完自己会在心中有个整体脉络,一定可以看懂的。个人认为,比《Android开发艺术探索》这本书要讲的更容易理解,至少对于初学者来说是的,我是看了这篇文章,再去看书,才明白书中讲的是是什么意思。
我看过之后,已经没有想要书写的欲望了,所以就把这篇文章的链接放在这,大家如果没有看过这篇文章,而又想学习这三大流程,可以一看。当你看完这篇文章,可以再看看我在下面的总结。
Android View的绘制流程
一、Measure##
我一直不明白这个过程为什么取名为measure,中文翻译成“测量”。一说测量,给我的直接感觉就是:应该先把View给画出来呀,然后我们才能测量这个View的宽和高。可是画是最后一步,这一步的目的是根据用户的布局设置,来确定View的宽和高,而不是我们认为的“测量”,总感觉用这个词不太贴切。所以,这一点有点搞不懂,如果有明白的同学,欢迎在底下留言告诉我。
Measure过程是这三个里面最复杂的了,过程我就不详解了,在我前面推荐的博客里面讲解的非常到位了。当我们看完之后,虽然对过程明白了,但相信我,用不了多长时间,只要不再接触,肯定会忘记的。所以,我们还是要在脑子里建立一个整体脉络,抛开一些细节,方便日后回忆。在我看来,记住两点就够了:一个是MeasureSpec和LayoutParam,这两个对象贯穿整个measure过程;还有一个就是在我们脑海中,建立一个ViewTree,MeasureSpec在整个ViewTree中是如何传递的,理清整个流程。
1、MeasureSpec和LayoutParam###
LayoutParam####
这个很好理解,LayoutParams就是我们在xml写的时候设置的layout_width和layout_height 转化而来的。而且从它的名称也能看出来,翻译过来就是“布局参数”。
MeasureSpec####
记住一个定义
MeasureSpec是由父View的MeasureSpec和子View的LayoutParams通过简单的计算,得出的一个针对子View的测量要求,这个测量要求就是MeasureSpec。
记住两张图
三点总结
1) EXACTLY与AT_MOST的区别就是:只要一个View的SpecMode是EXACTLY的,不论它的子View是match_parent还是wrap_content的,它的size都是固定的;如果一个View的SpecMode是AT_MOST的,那么分两种情况:如果它的子View是match_parent的,那么它的size就是指定的size,如果它的子View是wrap_content的,而且比parent小,那么它的size也会相应的跟随其子View变化。
2) MeasureSpec是测量要求,而不是测量结果,测量结果的确定需要子View的反馈,为什么?就因为有些View的SpecMode是AT_MOST的,也就是不确定的。如果都是EXACTLY的,那整个ViewTree遍历完,测量结果也就出来了,就不需要再层层反馈回去了。
3) 定义中说:MeasureSpec是由父View的MeasureSpec和子View的LayoutParams通过简单的计算得来的,那顶层View(也就是DecorView)的MeasureSpec是怎么来的?答案是:DecorView 的 MeasureSpec是Windows传过来的,其SpecMode是EXACTLY,size是屏幕的高和宽。
2、建立View树##
View树是什么东西?意思就是给我们的View建立一个布局图,而这个图是树形的。View的measure过程就是从根节点开始遍历整个树,MeasureSpec就在遍历的过程中,层层向下传递。前面我们说道,MeasureSpec只是测量要求,而不是测量结果。所以在遍历到叶子节点的时候,还会层层向上传递回来,这个过程就会给出最终的测量结果。
光说没有图,总是不好理解,而且记不住,所以我下面给出一张树图会更直观一些,这张图是我推荐的那篇博客里面的,我直接拿来展示了,细节大家可以去详细了解。
假设给出下面一段布局代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/linear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:background="@android:color/holo_blue_dark"
android:paddingBottom="70dp"
android:orientation="vertical">
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/material_blue_grey_800"
android:text="TextView"
android:textColor="@android:color/white"
android:textSize="20sp" />
<View
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="150dp"
android:background="@android:color/holo_green_dark" />
</LinearLayout>
那么整个布局对应的树图是这样的:
树形布局图之所以把这张图摆在这里,一是为了方便理解,其次还有利于记忆,每当我回想measure的整个过程时,我都会想象有个MeasureSpec在这张树中向下传递。
二、Layout##
Layout过程相对Measure过程要简单很多,看许多博客的篇幅就能看出来。这里我也不详解具体过程了,我再写一遍也没任何意义,这个过程都被别人写烂了,我还能写出花来不成?更何况我的水平还不如别人……
但我还是要写几点我的总结,因为看别人写的博客总是晕晕乎乎,大部分都是跟随源码,一步步往下写,自己心中如果没有一个整体的流程和大体的理解的话,很容易看着看着就懵了。
1、在measure过程中我们知道,是从跟View(也就是一个ViewGroup)向下遍历,直到遍历到叶节点(也就是一个具体的View),测量出其大小,然后向上回溯,逐步确定ViewGroup的大小。也就是先确定子View的大小,然后再确定的父View的大小。那我们就要想了,在layout过程中,是先确定子View的位置,还是父View的位置呢?答案是先确定父View的位置,或者说这个View自己的位置,然后层层向下传递,一一确定子View的位置。这个就牵扯到layout()方法和onLayout()方法了。
2、在Layout过程当中,频繁出现的两个方法:layout()和onLayout(),一开始就被弄糊涂了,搞不清楚它俩什么关系。后来捋顺了,简答总结来说就是:layout()方法确定View本身的位置,onLayout()则会确定所有子元素的位置。具体是怎么个流程呢?
3、一个View在layout()方法中完成自己的定位,然后就通过onLayout()方法去调用子View的layout()方法,子View又会通过自己的layout()方法来确定自己的位置,这样一层一层的传递下去就完成了整个View树的定位过程。
上面的三点总结是我在看这块内容之初,有些困惑我的地方,我也是通过看《Android开发艺术探索》搞明白的,虽然这本书没有把measure过程讲的清晰一些,但这里的问题还是提到了。所以在找参考时,还是尽量多看几个,每个作者写的侧重点都不同,有些疑问这里有解答,有些就在别处。
三、Draw##
推荐参考:Android View的绘制流程
完。
网友评论