一、基本概念:
1、Activity、Window、PhoneWindow、DecorView之间的关系:
Activity:很熟悉了,我们所能见到页面,不过多阐述;
Window:每个Activity都包含一个Window,也可以说每个Activity都包含一个Window的对象。Window是一个抽象基类,是Activity和View的交互接口,且只有一个实现类PhoneWindow。我们可以将其理解为一个载体,各种View都在这个载体上显示。
public class Activity extends ContextThemeWrappe{
private Window mWindow;
}
PhoneWindow:继承于Window类,是Window的具体实现。在PhoneWindow中持有着一个很重要的View对象,DecorView。
public class PhoneWindow extends Window{
private DecorView mDecor;
}
DecorView:是所有窗口的根View,继承于FrameLayout,并对其进行扩展修饰,比如添加TitleBar等。
2、MeasureSpc:
① 在 Google 官方文档中是这么定义:
A MeasureSpec encapsulates the layout requirements passed from parent to child. Each MeasureSpec represents a requirement for either the width or the height. A MeasureSpec is comprised of a size and a mode.
大概意思是:MeasureSpec 封装了从父View 传递给到子View的布局需求,每个MeasureSpec代表了view的宽度或高度的测量要求,即size(大小)和mode(模式)。
② MeasureSpc是一个32位的int值,高2位代表SpecMode(测量模式),低30位代表了SpecSize(测量大小)。
③ SpecMode分为三类:
· unSpecified:父容器不对子View有任何限制,要多大给多大,这种情况一般用于系统内部,表示一种测量的状态。(可忽略)
· Exactly:父容器已经测量出子View的大小。对应是view的LayoutParams的match_parent或者具体数值。
· At_most:父容器指定了一个可用大小的SpecSize,view的大小不能够大于这个值,对应wrap_content。
④ 重点:View的MeasureSpec,由父 view 的MeasureSpec加上子 View 的自己的 LayoutParams,通过相应的规则转化,共同决定。
从父View的角度分析MeasureSpec的转化规则:
- 当父View的SpecMode是Exactly的时候,即父View的大小是确定的种情况:
①若子View宽/高为match_parent,则子View的SpecMode也是Exactly模式,且其大小是父容器的剩余空间。
②若子View宽/高为wrap_parent,则子View的SpecMode为At_most模式,且其大小不超过父容器的剩余空间。
③若子View宽/高为具体数值,则子View的SpecMode为Exactly模式,大小就是该具体数值。 - 当父View的SpecMode是At_most的时候,即父View的大小是不确定的情况:
①若子View宽/高为match_parent,则子View的SpecMode也是At_most模式,且其大小不超过父容器的剩余空间。
②若子View宽/高为wrap_parent,则子View的SpecMode为At_most模式,且其大小不超过父容器的剩余空间。
③若子View宽/高为固定宽高,则子View的SpecMode为Exactly模式,大小就是该固定宽高。
从子View的角度分析MeasureSpec的转化规则:
- 当子View的宽/高采用固定宽高,无论父容器的SpecMode是什么,子View的SpecMode都是Exactly,其大小遵循LayoutParams的大小。
- 当子VIew的宽/高采用match_parent:
①若父容器的SpecMode为Exactly,那么子View的SpecMode也为Exactly,且其大小是父容器的剩余空间。
②若父容器的SpecMode为At_most,那么子View的SpecMode也为At_most,且其大小是父容器的剩余空间。 - 当子View的宽/高采用wrap_content,无论父容器是什么模式,子View的模式总是At_most,且大小不能超过父容器的剩余空间。
二、流程概述 :
view的绘制流程概括的描述为三部曲:measure -> layout -> draw
三、流程详述 :
1、ViewRootImpl (绘制入口):
Window的添加过程需要通过WindowManager的addView来实现。在addView时会创建一个ViewRootImpl对象,然后通过该对象可的setView完成Window的添加,在setView中调用requestLayout()完成异步刷新请求,在requestLayout中调用performTraversals来完场view的绘制。
总结:WindowManagerImpl -> addView -> new ViewRootImpl -> setView -> requestLayout -> performTraversals
绘制流程三部曲的核心入口就在 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);
......
}
2、measure(测量):
作用:measure 主要功能就是测量设置 View 的大小。
该过程由view的measure方法来完成,该方法是 final 类型,子类不能覆盖,在measure方法中会调用 onMeasure()方法,我们也可以复写 onMeasure()方法去测量设置 View 的大小。
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
.....
onMeasure(widthMeasureSpec, heightMeasureSpec);
.....
}
onMeasure(这段代码就是measure过程的核心代码),接下来对onMeasure重点刨析:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(
getDefaultSize(getSuggestedMinimumWidth()