本文源码基于Android 11
初学者可能认为执行SetContentView(int layoutResID)之后就马上开始执行绘制流程了,其实并没有,我们来看看SetContentView(int layoutResID)做了什么:
java
public class Activity{
private Window mWindow;
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
final void attach(...) {
mWindow = new PhoneWindow(this, window, activityConfigCallback);
}
public Window getWindow() {
return mWindow;
}
}
其中mWindow是PhoneWindow的实例,接下来调用PhoneWindow的setContentView()方法:
java
public class PhoneWindow extends Window{
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
@Override
public void setContentView(int layoutResID) {
...
// 第一次mContentParent为null
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
//hasFeature()方法默认返回false
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
//把layoutResID对应的布局添加到mContentParent中
mLayoutInflater.inflate(layoutResID, mContentParent);
}
...
}
private void installDecor() {
//mDecor即DecorView
if (mDecor == null) {
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
//mContentParent来自generateLayout()
mContentParent = generateLayout(mDecor);
}
...
}
protected ViewGroup generateLayout(DecorView decor) {
...
int layoutResource;
if ((features & (1 << FEATURE_NO_TITLE)) == 0)){
...
}else if ((features & (1 << FEATURE_ACTION_BAR)) != 0)){
...
}else{
// Embedded, so no decoration is needed.
layoutResource = R.layout.screen_simple;
}
mDecor.startChanging();
//将layoutResource对应的布局添加到DecorView中
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
...
//mContentParent对应的布局id为com.android.internal.R.id.content;
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
...
return contentParent;
}
}
其中R.layout.screen_simple的布局如下:
xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<ViewStub android:id="@+id/action_mode_bar_stub"
android:inflatedId="@+id/action_mode_bar"
android:layout="@layout/action_mode_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?attr/actionBarTheme" />
<!--这个就是上面的mContentParent-->
<FrameLayout
android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foregroundInsidePadding="false"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
由以上代码可知,setContentView(int layoutResID)只是把layoutResID对应的布局添加到DecorView中id为content的FrameLayout里面,还没有开始执行绘制流程。
那么执行绘制流程是从什么时候开始的呢?真正执行绘制流程在Activity的onResume()生命周期方法执行之后,我们来看看ActivityThread中的代码:
java
public final class ActivityThread{
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) {
...
//performResumeActivity()里面执行了Activity的onResume()方法
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
...
final Activity a = r.activity;
//decor即DecorView, 每个PhoneWindow对应一个DecorView
View decor = r.window.getDecorView();
ViewManager wm = a.getWindowManager();
wm.addView(decor, l);
...
}
}
上面的代码中执行了wm.addView(decor, l),wm来源于Activity的getWindowManager()方法:
java
public class Activity{
private WindowManager mWindowManager;
final void attach(...){
mWindowManager = mWindow.getWindowManager();
}
/** Retrieve the window manager for showing custom windows. */
public WindowManager getWindowManager() {
return mWindowManager;
}
}
最终追溯到Window的getWindowManager()方法:
java
public abstract class Window{
private WindowManager mWindowManager;
/**
* Set the window manager for use by this Window to, for example,
* display panels. This is <em>not</em> used for displaying the
* Window itself -- that must be done by the client.
*
* @param wm The window manager for adding new windows.
*/
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
/**
* Return the window manager allowing this Window to display its own
* windows.
*
* @return WindowManager The ViewManager.
*/
public WindowManager getWindowManager() {
return mWindowManager;
}
}
由以上代码可知wm即WindowManagerImpl,所以实际调用的是WindowManagerImpl的addView()方法:
java
public final class WindowManagerImpl implements WindowManager {
//mGlobal是WindowManagerGlobal的单例
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
}
}
接着执行WindowManagerGlobal的addView()方法:
java
public final class WindowManagerGlobal {
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
private static WindowManagerGlobal sDefaultWindowManager;
public static WindowManagerGlobal getInstance() {
synchronized (WindowManagerGlobal.class) {
if (sDefaultWindowManager == null) {
sDefaultWindowManager = new WindowManagerGlobal();
}
return sDefaultWindowManager;
}
}
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
ViewRootImpl root;
...
//创建ViewRootImpl实例
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView, userId);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
接下来执行ViewRootImpl的setView()方法:
java
public final class ViewRootImpl{
final IWindowSession mWindowSession;
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
...
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
//在添加到Window Manager之前执行绘制流程
requestLayout();
...
res = mWindowSession.addToDisplayAsUser(...) //通过Binder将View添加到WMS中
...
}
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
//检查线程
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
void scheduleTraversals() {
//添加了mTraversalScheduled标志位,所以连续两次setText()只会执行一次绘制流程
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//添加同步屏障
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// 提交给编舞者,会在下一帧绘制时调用mTraversalRunnable,运行其run()方法
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
...
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
//移除同步屏障
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
...
performTraversals();
}
}
private void performTraversals() {
......
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
...
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); //测量
...
performLayout(lp, mWidth, mHeight); //布局
...
performDraw(); //绘制
...
}
}
在performTraversals()方法中,首先通过getRootMeasureSpec()方法获取最外层根视图的childWidthMeasureSpec和childHeightMeasureSpec。
什么是MeasureSpec? MeasureSpec是一个32位的整形,高2位表示测试模式SpecMode,低30位表示测量规格SpecSize,它是View的静态内部类,用来说明如何测量这个View,核心代码如下:
java
public static class MeasureSpec {
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0X3 << MODE_SHIFT;
// 不指定测量模式, 父视图没有限制子视图的大小,子视图可以是想要
// 的任何尺寸,通常用于系统内部,应用开发中基本用不到。
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
// 精确测量模式,视图宽高指定为match_parent或具体数值时生效,
// 表示父视图已经决定了子视图的精确大小,这种模式下View的测量
// 值就是SpecSize的值。
public static final int EXACTLY = 1 << MODE_SHIFT;
// 最大值测量模式,当视图的宽高指定为wrap_content时生效,此时
// 子视图的尺寸可以是不超过父视图允许的最大尺寸的任何尺寸。
public static final int AT_MOST = 2 << MODE_SHIFT;
// 根据指定的大小和模式创建一个MeasureSpec
public static int makeMeasureSpec(int size, int mode) {
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
//MODE_MASK为:11000000 00000000 00000000 00000000
//~MODE_MASK为:00111111 11111111 11111111 11111111
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
}
makeMeasureSpec()通过size和mode来构造MeasureSpec,当然也可以通过MeasureSepc来获取size和mode,代码如下:
java
public static int getMode(int measureSpec) {
return (measureSpec & MODE_MASK);
}
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
前面通过getRootMeasureSpec()方法来获取DecorView的MeasureSpec,代码如下:
java
/**
* Figures out the measure spec for the root view in a window based on it's
* layout params.
*
* @param windowSize
* The available width or height of the window
*
* @param rootDimension
* The layout params for one dimension (width or height) of the
* window.
*
* @return The measure spec to use to measure the root view.
*/
//这个方法是来获取RootView的MeasureSpec的
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;
case ViewGroup.LayoutParams.WRAP_CONTENT:
// Window can resize. Set max size for root view.
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
default:
// Window wants to be an exact size. Force root view to be that size.
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}
由以上代码可知,DecorView的MeasureSpec由窗口尺寸(windowSize)和自身的LayoutParams共同决定。
接下来把DecorView的MeasureSpec传给了performMeasure()方法,代码如下:
java
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
...
try {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
接下来执行View的measure()方法:
java
public class View{
//final方法,子类不可重写
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
...
onMeasure(widthMeasureSpec, heightMeasureSpec);
...
}
}
由于DecorView是FrameLayout的子类,接下来执行FrameLayout的onMeasure()方法(LinearLayout、RelativeLayout等ViewGroup的子类都会根据自己的特性重写onMeasure()方法):
java
public class FrameLayout extends ViewGroup {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
int maxHeight = 0;
int maxWidth = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
//先测量子View的宽高
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
//获取宽的最大值
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
//获取高的最大值
maxHeight = Math.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
childState = combineMeasuredStates(childState, child.getMeasuredState());
if (measureMatchParentChildren) {
if (lp.width == LayoutParams.MATCH_PARENT ||
lp.height == LayoutParams.MATCH_PARENT) {
mMatchParentChildren.add(child);
}
}
}
}
...
//测量自己的宽高
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT));
}
}
其中measureChildWithMargins()方法在ViewGroup中:
java
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
//获取子视图的LayoutParams
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
//通过父视图的测量规格以及子视图本身的LayoutParams来共同决定子视图的测量规格
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
//执行子View的measure()方法
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
由以上代码可知,子View的宽高由父View的测量规格和自己的LayoutParams共同决定。
其中子View的childWidthMeasureSpec通过getChildMeasureSpec()方法拿到,代码如下:
java
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
/**
* Does the hard part of measureChildren: figuring out the MeasureSpec to
* pass to a particular child. This method figures out the right MeasureSpec
* for one dimension (height or width) of one child view.
*
* The goal is to combine information from our MeasureSpec with the
* LayoutParams of the child to get the best possible results. For example,
* if the this view knows its size (because its MeasureSpec has a mode of
* EXACTLY), and the child has indicated in its LayoutParams that it wants
* to be the same size as the parent, the parent should ask the child to
* layout given an exact size.
*
* @param spec The requirements for this view
* @param padding The padding of this view for the current dimension and
* margins, if applicable
* @param childDimension How big the child wants to be in the current
* dimension
* @return a MeasureSpec integer for the child
*/
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;
switch (specMode) {
// Parent has imposed an exact size on us
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size. So be it.
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent has imposed a maximum size on us
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
// Child wants a specific size... so be it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size, but our size is not fixed.
// Constrain child to not be bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent asked to see how big we want to be
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
// Child wants a specific size... let him have it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size... find out how big it should
// be
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size.... find out how
// big it should be
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
//noinspection ResourceType
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
}
普通View的MeasureSpec的创建规则如下:
接下来继续执行子View的measure()方法,如果子View为普通的View,则执行View的onMeasure()方法,代码如下:
java
public class View{
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
...
setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
//MeasureSpec.AT_MOST和MeasureSpec.EXACTLY都返回specSize
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
protected int getSuggestedMinimumHeight() {
//背景为null, 则返回mMinHeight;否则返回mMinHeight和mBackground.getMinimumHeight()中较大的一个
return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());
}
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
}
setMeasuredDimension()方法最终调用了setMeasuredDimensionRaw()对mMeasuredWidth和mMeasuredHeight赋值,一旦这两个变量被赋值意味着该View的测量工作结束。
上面的代码中我们看到View的mMeasuredWidth和mMeasuredHeight通过getDefaultSize()方法来获取,在SpecMode为MeasureSpec.AT_MOST和MeasureSpec.EXACTLY时都返回了specSize,这就是为什么在自定义View中如果直接继承View并配置为wrap_content时实际效果是该自定义View把剩下的空间都占满了的原因。这种情况下我们需要重写View的onMeasure方法,代码如下:
java
public class MyView extends View {
public MyView(Context context) {
this(context, null);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//你可以自己给mWidth和mHeight赋值为实际包裹内容的宽高
int mWidth = 100;
int mHeight = 100;
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(mWidth, mHeight);
} else if (widthSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(mWidth, heightSpecSize);
} else if (heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(widthSpecSize, mHeight);
}
}
}
整个View视图是一个树结构的视图: measure过程就是从DecorView开始自顶向下遍历递归调用view的measure()方法(measure()中又调用onMeasure()方法)并最终对mMeasuredWidth和mMeasuredHeight赋值的过程。
接下来分析performLayout()方法如下:
java
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight) {
...
final View host = mView;
...
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
...
}
ViewGroup的layout()方法为final类型,不可以重写:
java
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
@Override
public final void layout(int l, int t, int r, int b) {
...
super.layout(l, t, r, b);
...
}
}
接着调用View的layout()方法:
java
public class View{
public void layout(int l, int t, int r, int b) {
......
//实质都是调用setFrame方法把参数分别赋值给mLeft、mTop、mRight和mBottom这几个变量
//判断View的位置是否发生过变化,以确定有没有必要对当前的View进行重新layout
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
//需要重新layout
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
//调用onLayout()方法
onLayout(changed, l, t, r, b);
......
}
......
}
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
...
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
changed = true;
...
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
...
}
return changed;
}
}
View的onLayout()方法是一个空方法,ViewGroup的onLayout()方法是一个抽象方法,意味着ViewGroup的子类都必须重写这个方法。DecorView是FrameLayout的子类,接下来调用FrameLayout的onLayout()方法:
java
public class FrameLayout extends ViewGroup {
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
//确定子View的4个点在父View中的位置
layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
...
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
一般情况下layout过程会参考measure过程中计算得到的mMeasuredWidth和mMeasuredHeight来安排子View在父View中显示的位置,但这不是必须的,measure过程得到的结果可能完全没有实际用处,特别是对于一些自定义的ViewGroup,其子View的个数、位置和大小都是固定的,这时候我们可以忽略整个measure过程,只在layout过程中传入的4个参数来安排每个子View的具体位置。
到这里就不得不提一下getWidth()、getHeight()和getMeasuredWidth()、getMeasuredHeight()这两对方法之间的区别,在View源码中这些方法的实现如下:
java
public class View{
public final int getMeasuredWidth() {
return mMeasuredWidth & MEASURED_SIZE_MASK;
}
public final int getMeasuredHeight() {
return mMeasuredHeight & MEASURED_SIZE_MASK;
}
public final int getWidth() {
return mRight - mLeft;
}
public final int getHeight() {
return mBottom - mTop;
}
public final int getLeft() {
return mLeft;
}
public final int getRight() {
return mRight;
}
public final int getTop() {
return mTop;
}
public final int getBottom() {
return mBottom;
}
}
从这些方法的实现中可以看出:getMeasuredWidth()、getMeasuredHeight()必须在onMeasure()执行之后才有效;getWidth()与getHeight()方法必须在layout(int l, int t, int r, int b)执行之后才有效。
从上面的分析中可以看出,layout过程是从顶层父View向子View的递归调用view的layout()方法并最终确定mLeft、mTop、mRight、mBottom四个顶点的过程。
接下来分析performDraw()方法:
java
private void performDraw() {
...
boolean canUseAsync = draw(fullRedrawNeeded);
...
}
private boolean draw(boolean fullRedrawNeeded) {
...
mAttachInfo.mThreadedRenderer.draw() //硬件加速绘制
...
drawSoftware() //软件绘制
...
}
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
...
mView.draw(canvas);
...
}
接下来调用View的draw()方法:
java
public class View{
public void draw(Canvas canvas) {
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
* 7. If necessary, draw the default focus highlight
*/
// Step 1, draw the background, if needed
// 步骤1.绘制背景
int saveCount;
drawBackground(canvas);
// skip step 2 & 5 if possible (common case)
// 步骤2.如果需要的话,保存canvas的图层,为fading做准备
// 步骤5.如果需要的话,绘制fading edges和恢复图层
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the content
// 步骤3.绘制View的内容
onDraw(canvas);
// Step 4, draw the children
// 步骤4.绘制子View
dispatchDraw(canvas);
drawAutofilledHighlight(canvas);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// Step 6, draw decorations (foreground, scrollbars)
// 步骤6.绘制View的装饰 (foreground, scrollbars)
onDrawForeground(canvas);
// Step 7, draw the default focus highlight
// 步骤7.绘制默认的焦点高亮显示
drawDefaultFocusHighlight(canvas);
if (isShowingLayoutBounds()) {
debugDrawFocus(canvas);
}
// we're done...
return;
}
...
}
}
View的onDraw()方法是一个空方法,说明View默认不会绘制任何内容,真正的绘制都需要自己在子类中实现。在ViewGroup中对dispatchDraw()方法进行了重写,代码如下:
java
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
protected void dispatchDraw(Canvas canvas) {
...
final int childrenCount = mChildrenCount;
final View[] children = mChildren;
...
for (int i = 0; i < childrenCount; i++) {
while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
final View transientChild = mTransientViews.get(transientIndex);
if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
transientChild.getAnimation() != null) {
//调用drawChild()方法
more |= drawChild(canvas, transientChild, drawingTime);
}
}
}
...
// Draw any disappearing views that have animations
if (mDisappearingChildren != null) {
...
for (int i = disappearingCount; i >= 0; i--) {
final View child = disappearingChildren.get(i);
more |= drawChild(canvas, child, drawingTime);
}
}
...
}
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
}
dispatchDraw()方法里面遍历每个子View,调用了子View的draw()方法。