到目前为止,基本把Measure 主要原理都过了一遍,接下来我们会结合实例来讲解整个match的过程,首先看下面的代码:
<?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” />
上面的代码对于出来的布局是下面的一张图
对于上面图可能有些不懂,这边做下说明:
整个图是一个DecorView,DecorView可以理解成整个页面的根View,DecorView是一个FrameLayout,包含两个子View,一个id=statusBarBackground的View和一个是LineaLayout,id=statusBarBackground的View,我们可以先不管(我也不是特别懂这个View,应该就是statusBar的设置背景的一个控件,方便设置statusBar的背景),而这个LinearLayout比较重要,它包含一个title和一个content,title很好理解其实就是TitleBar或者ActionBar,content 就更简单了,setContentView()方法你应该用过吧,android.R.id.content 你应该听过吧,没错就是它,content是一个FrameLayout,你写的页面布局通过setContentView加进来就成了content的直接子View。
整个View的布局图如下:
这张图在下面分析measure,会经常用到,主要用于了解递归的时候view 的measure顺序
注:
1、 header的是个ViewStub,用来惰性加载ActionBar,为了便于分析整个测量过程,我把Theme设成NoActionBar,避免ActionBar 相关的measure干扰整个过程,这样可以忽略掉ActionBar 的测量,在调试代码更清晰。
2、包含Header(ActionBar)和id/content 的那个父View,我不知道叫什么名字好,我们姑且叫它ViewRoot(看上图),它是垂直的LinearLayout,放着整个页面除statusBar 的之外所有的东西,叫它ViewRoot 应该还ok,一个代号而已。
既然我们知道整个View的Root是DecorView,那么View的绘制是从哪里开始的呢,我们知道每个Activity 均会创建一个 PhoneWindow对象,是Activity和整个View系统交互的接口,每个Window都对应着一个View和一个ViewRootImpl,Window和View通过ViewRootImpl来建立联系,对于Activity来说,ViewRootImpl是连接WindowManager和DecorView的纽带,绘制的入口是由ViewRootImpl的performTraversals方法来发起Measure,Layout,Draw等流程的。
我们来看下ViewRootImpl的performTraversals 方法:
private void performTraversals() {
…
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
…
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
…
mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
…
mView.draw(canvas);
…
}
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT:
// Window can’t resize. Force root view to be windowSize.
measureSpec = MeasureSpec.makeMeasureSpec(windowSize,MeasureSpec.EXACTLY);
break;
…
}
return measureSpec;
}
performTraversals 中我们看到的mView其实就是DecorView,View的绘制从DecorView开始, 在mView.measure()的时候调用getRootMeasureSpec获得两个MeasureSpec做为参数,getRootMeasureSpec的两个参数(mWidth, lp.width)mWith和mHeight 是屏幕的宽度和高度, lp是WindowManager.LayoutParams,它的lp.width和lp.height的默认值是MATCH_PARENT,所以通过getRootMeasureSpec 生成的测量规格MeasureSpec 的mode是MATCH_PARENT ,size是屏幕的高宽。
因为DecorView 是一个FrameLayout 那么接下来会进入FrameLayout 的measure方法,measure的两个参数就是刚才getRootMeasureSpec的生成的两个MeasureSpec,DecorView的测量开始了。
首先是DecorView 的 Me