Android View系列(一)——View源码分析——DecorView


title: Android View系列(一)——View源码分析——DecorView
tag: Android源码
category: Android


View源码分析——DecorView为例

基本概念:ViewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带。View的三大流程measure、layout、draw都是通过ViewRoot来完成。在ActivityThread中,当Activity对象被创建,会将DecorView添加到Window,同时会创建ViewRootImpl对象,并将两个关联

在Activity创建完成后,通过attach()方法将Context、Application等绑定到Activity中,同时实例化一个PhoneWindow,并建立和WindowManager的关联,接着回调onCreate()方法,同时我们这里设置的setContentView()会调用到Activity的setContentView(),接着会调用其Window的setContentView(),最后就调用到PhoneWindow中,在PhoneWindow如果没有初始化DecorView就会先进行初始化DecorView,然后通过LayoutInflater去inflate()我们设置的view,在LayoutInflater中通过xml解析器,根据标签递归解析我们的布局,然后生成对应的view添加到DecorView中的parentView(这个是用于放置我们设置的view的地方),这样就得到了整个View树保存在DecorView中(DecorView保存在Windows中);

在ActivityThread的handleResumeActivity方法中,通过获取到WindowManager,然后在后面通过addView方法,将decor添加进去,WindowManager的实现类是WindowManagerImpl,这又是Window的添加过程,在这个过程中,有一个requestLayout方法(相关过程参考我的Window源码分析

@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        //这就是View绘制的入口
        scheduleTraversals();
    }
}

接着看内部的scheduleTraversals方法

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        //这里通过
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        //mTraversalRunnable是一个Runnable
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

这个代码看着可能就有点迷茫了,目前也没看太懂,只知道mTraversalRunnable是一个Runnable对象,而在它的run方法呢看到我们后续要分析的方法,所以这里的分析就暂时放放(感觉是通过mChoreographer来调用mTraversalRunnable中的run方法,但是不知道mChoreographer是用来干什么的),好吧,遇到问题就先放放,欢迎知道的人答疑(期待您的看法)

那就接着看mTraversalRunnable中的run方法吧

final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();
    }
}
//这个对象还是final的哦
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

run方法中就只有一行代码,就是调用了ViewRootImpl中的doTraversal方法,接着看

void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
        ...
        performTraversals();
        ...
    }
}

这个方法中呢,重点就只是performTraversals咯,接着看ViewRootImpl中内部的performTraversals方法吧

private void performTraversals() {
    // cache mView since it is used so much below...
    final View host = mView;
    ...
    boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
    if (layoutRequested) {
        final Resources res = mView.getContext().getResources();
        ...
        // Ask host how big it wants to be
        //在测量的开始,会去询问到底要多大的空间,对于DecorView来说,就是屏幕的尺寸,desiredWindowWidth、desiredWindowHeight是屏幕的宽和高
        windowSizeMayChange |= measureHierarchy(host, lp, res,
                desiredWindowWidth, desiredWindowHeight);
    }
    ...
    if (mFirst || windowShouldResize || insetsChanged ||
            viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
        ...
        if (!mStopped || mReportNextDraw) {
            ...
            if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
                    || mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
                    updatedConfiguration) {
                ...
                 // Ask host how big it wants to be
                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                ....
                //是否需要重新测量
                if (measureAgain) {
                    ...
                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                }
                ...
            }
        }
    } else {
        ...
    }

    final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
    ...
    if (didLayout) {
        performLayout(lp, mWidth, mHeight);
        ...
    }
    ...
    if (!cancelDraw && !newSurface) {
        ...
        performDraw();
    } else {
        ...
    }
    ...
}

View的绘制流程是从ViewRoot的performTraversals开始的,这个就是我们View绘制流程的开始(我知道这个方法很关键,但是也不用这么多吧),代码很长,就跟你改不完的bug一样多。好了,赶紧抓重点,在这个方法中我们找到了performMeasure、performLayout、performDraw这三个方法,这好像就是View绘制的三大流程(但好像还有点不像啊),这三个方法分别完成顶级View(DecorView)的measure、layout、draw过程,在performMeasure中回调用measure方法,在measure方法中又会调用onMeasure方法,在onMeasure方法中则会对所有子View进行measure过程,这时候measure流程就从父容器传到子View中了,这样就完成了一次measure过程。接着子View会重复父容器的过程。performLayout和performDraw的传递流程跟performMeasure基本类似,唯一不同的是performDraw在draw方法是通过dispatchDraw实现的

接下来就一一分析measure、layout、draw吧

MeasureSpec

在分析measure之前,先来了解MeasureSpec是做什么的把

MeasureSpec决定了View的测量过程,是measure过程重要的一个类

MeasureSpec是一个32位int值,高2位代表SpecMode(测量模式),低30位代表SpecSize(在某种测量模式下的测量大小)

public static class MeasureSpec {
    private static final int MODE_SHIFT = 30;
    private static final int MODE_MASK  = 0x3 << MODE_SHIFT;

    /** @hide */
    @IntDef({UNSPECIFIED, EXACTLY, AT_MOST})
    @Retention(RetentionPolicy.SOURCE)
    public @interface MeasureSpecMode {}

    public static final int UNSPECIFIED = 0 << MODE_SHIFT;
    public static final int EXACTLY     = 1 << MODE_SHIFT;
    public static final int AT_MOST     = 2 << MODE_SHIFT;
    //构建MeasureSpec
    public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,
                                      @MeasureSpecMode int mode) {
        if (sUseBrokenMakeMeasureSpec) {
            return size + mode;
        } else {
            return (size & ~MODE_MASK) | (mode & MODE_MASK);
        }
    }

    public static int makeSafeMeasureSpec(int size, int mode) {
        if (sUseZeroUnspecifiedMeasureSpec && mode == UNSPECIFIED) {
            return 0;
        }
        return makeMeasureSpec(size, mode);
    }

    @MeasureSpecMode
    public static int getMode(int measureSpec) {
        //noinspection ResourceType
        return (measureSpec & MODE_MASK);
    }

    public static int getSize(int measureSpec) {
        return (measureSpec & ~MODE_MASK);
    }

    static int adjust(int measureSpec, int delta) {
        final int mode = getMode(measureSpec);
        int size = getSize(measureSpec);
        if (mode == UNSPECIFIED) {
            // No need to adjust size for UNSPECIFIED mode.
            return makeMeasureSpec(size, UNSPECIFIED);
        }
        size += delta;
        if (size < 0) {
            Log.e(VIEW_LOG_TAG, "MeasureSpec.adjust: new size would be negative! (" + size +
                    ") spec: " + toString(measureSpec) + " delta: " + delta);
            size = 0;
        }
        return makeMeasureSpec(size, mode);
    }
    ...
}

MeasureSpec是View的内部类,里面代码并不多。MeasureSpec通过makeMeasureSpec方法将SpecMode和SpecSize打包成一个int值类避免过多的对象内存分配,然后也提供了解包的方法getMode、getSize来分别获取SpecMode和SpecSize。所以MeasureSpec这个类就是一个工具类,用来将SpecMode和SpecSize打包成一个int值,然后通过这个int值和对应的方法,可以获取对应的SpecMode和SpecSize

SpecMode有三类:

  • UNSPECIFIED
    父容器对View不会有任何限制,要多大给多大,一般用于系统内部,表示一种测量状态
  • EXACTLY
    父容器已经检测出View所需要的精确大小,这个时候View的最终大小就是SpecSize指定的值,对应于LayoutParams中的match_parent和具体的数值这两种模式
  • AT_MOST
    父容器指定了一个可用大小的SpecSize,View的大小不能大于这个值,具体是什么看不同View的具体实现。对应于LayoutParams中的wrap_content

MeasureSpec和LayoutParams的关系
系统内部是通过MeasureSpec来进行View测量,但我们可以给View设置LayoutParams,在View测量的时候,系统会将LayoutParams在父容器的约束下转换成对应的MeasureSpec,然后再根据这个MeasureSpec来确定View测量后的宽/高。(注意:自身的LayoutParams和父容器一起才能决定View的MeasureSpec,从而决定View的宽/高;有一个例外就是顶级View——DecorView的MeasureSpec是由窗口的尺寸和自身的LayoutParams来决定的),MeasureSpec确定后,onMeasure中就可以确定View的宽/高了

对于DecorView来说,通过performMeasure(childWidthMeasureSpec, childHeightMeasureSpec)来进行测量的,在这之前,会通过windowSizeMayChange |= measureHierarchy(host, lp, res, desiredWindowWidth, desiredWindowHeight);这行代码先询问要多大的空间,屏幕的尺寸是否修改,传进的参数是屏幕的尺寸和lp,lp是WindowManager.LayoutParams类型,是窗口的参数

private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,
        final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {
    int childWidthMeasureSpec;
    int childHeightMeasureSpec;
    boolean windowSizeMayChange = false;

    if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(mTag,
            "Measuring " + host + " in display " + desiredWindowWidth
            + "x" + desiredWindowHeight + "...");

    boolean goodMeasure = false;
    ...
    if (!goodMeasure) {
        //根据屏幕的尺寸获取自身的MeasureSpec
        childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
        childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
            windowSizeMayChange = true;
        }
    }
    ...
    return windowSizeMayChange;
}

childWidthMeasureSpec和childHeightMeasureSpec的赋值是通过getTootMeasureSpec方法得到的,根据屏幕的尺寸来获取的,那接着看看这个getRootMeasureSpec方法

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;
}

这里就是产生MeasureSpec的地方了,LayoutParams.MATCH_PARENT对应就是窗口的大小;LayoutParams.WRAP_CONTENT就是最大模式,大小不定,不能超过窗口的大小;固定大小就是指定大小。

对于DecorView这个顶级View来说,就是通过屏幕的尺寸和窗口的LayoutParams来获取MeasureSpec的

measure

ViewGroup的测量,这里以DecorView为例

measure过程从ViewRootImpl的performMeasure开始

private void performTraversals() {
    ...
    //final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();
    WindowManager.LayoutParams lp = mWindowAttributes;
    int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
    int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
    if (mView == null) {
        return;
    }
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
    try {
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

首先看一下传进来的参数是什么?

childWidthMeasureSpec和childHeightMeasureSpec都是通过getRootMeasureSpec()方法来获取的,这个方法前面我们讲过了([MeasureSpec](http://blog.penghesheng.cn/2019/04/25/Android View系列(一)——View源码分析/#MeasureSpec)),就是根据传入的size来判定对应的mode,然后组装成一个MeasureSpec。以childWidthMeasureSpec来讲,那么这里我们传递的参数mWidthlp.width是什么呢?mWidth是通过mWidth = frame.width();来获取的,其实就是Window的最大值;lp.width是WindowAttributes,代码中注释了具体来源,就是WindowManager.LayoutParams;总的来说,这里由于是DecorView,所以自然就是根据屏幕来获取的这个MeasureSpec

接着看performMeasure(),mView是在ViewRootImpl实例化的时候传进的DecorView,DecorView本身也是一个View,接着调用View的measure方法测量child的width和height

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    ...

    if (forceLayout || needsLayout) {
        ...
        if (cacheIndex < 0 || sIgnoreMeasureCache) {
            // measure ourselves, this should set the measured dimension flag back
            onMeasure(widthMeasureSpec, heightMeasureSpec);
            mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
        } else {
            long value = mMeasureCache.valueAt(cacheIndex);
            // Casting a long to int drops the high 32 bits, no mask needed
            setMeasuredDimensionRaw((int) (value >> 32), (int) value);
            mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
        }
        ...
    }
    ...
}

measure方法是一个final方法,就意味着子类不能重写,在View的measure方法中,会去调用onMeasure方法测量自己本身,由于这里分析的View是DecorView,它是一个ViewGroup,所以会调用自己重写的onMeasure方法,然后会去测量子View

子View的测量(普通View)

注意:因为这里分析的DecorView,DecorView也是一个ViewGroup,会在onMeasure方法中对所有子View进行测量

DecorView的onMeasure方法

 @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
    final boolean isPortrait =
            getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT;

    final int widthMode = getMode(widthMeasureSpec);
    final int heightMode = getMode(heightMeasureSpec);

    boolean fixedWidth = false;
    mApplyFloatingHorizontalInsets = false;
    //如果SpecMode不是EXACTLY的,则需要在这里调整为EXACTLY
    if (widthMode == AT_MOST) {
        final TypedValue tvw = isPortrait ? mWindow.mFixedWidthMinor : mWindow.mFixedWidthMajor;
        if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
            final int w;
            //根据DecorView属性,计算出DecorView需要的宽度
            if (tvw.type == TypedValue.TYPE_DIMENSION) {
                w = (int) tvw.getDimension(metrics);
            } else if (tvw.type == TypedValue.TYPE_FRACTION) {
                w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels);
            } else {
                w = 0;
            }
            if (DEBUG_MEASURE) Log.d(mLogTag, "Fixed width: " + w);
            final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
            if (w > 0) {
                //根据上面计算出来的需要的宽度生成新的MeasureSpec用于DecorView的测量流程
                widthMeasureSpec = MeasureSpec.makeMeasureSpec(
                        Math.min(w, widthSize), EXACTLY);
                fixedWidth = true;
            } else {
                widthMeasureSpec = MeasureSpec.makeMeasureSpec(
                        widthSize - mFloatingInsets.left - mFloatingInsets.right,
                        AT_MOST);
                mApplyFloatingHorizontalInsets = true;
            }
        }
    }

    mApplyFloatingVerticalInsets = false;
    //如果SpecMode不是EXACTLY的,则需要在这里调整为EXACTLY
    if (heightMode == AT_MOST) {
        final TypedValue tvh = isPortrait ? mWindow.mFixedHeightMajor
                : mWindow.mFixedHeightMinor;
        if (tvh != null && tvh.type != TypedValue.TYPE_NULL) {
            final int h;
            if (tvh.type == TypedValue.TYPE_DIMENSION) {
                h = (int) tvh.getDimension(metrics);
            } else if (tvh.type == TypedValue.TYPE_FRACTION) {
                h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels);
            } else {
                h = 0;
            }
            if (DEBUG_MEASURE) Log.d(mLogTag, "Fixed height: " + h);
            final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
            if (h > 0) {
                heightMeasureSpec = MeasureSpec.makeMeasureSpec(
                        Math.min(h, heightSize), EXACTLY);
            } else if ((mWindow.getAttributes().flags & FLAG_LAYOUT_IN_SCREEN) == 0) {
                heightMeasureSpec = MeasureSpec.makeMeasureSpec(
                        heightSize - mFloatingInsets.top - mFloatingInsets.bottom, AT_MOST);
                mApplyFloatingVerticalInsets = true;
            }
        }
    }
    ...
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    ...
}

在DecorView的onMeasure方法中,会先进行SpecMode的调整,调整为EXACTLY,然后回创建出新的MeasureSpec(宽高都一样的逻辑),接着就调用了父类的onMeasure方法

接着看父类FrameLayout的onMeasure方法

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int count = getChildCount();
    // 如果测量规格有一个不是精确值,这里就为true
    final boolean measureMatchParentChildren =
            MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
            MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
    mMatchParentChildren.clear();

    int maxHeight = 0;
    int maxWidth = 0;
    int childState = 0;
    //遍历所有子View
    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();
            //由于子布局占用的尺寸除了自身宽高之外,还包含了其距离父布局的边界的值,所以需要加上左右Margin值
            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());
            //当前的FrameLayout的MeasureSpec不都是EXACTLY,且其子View为MATCH_PARENT,
          //则子View保存到mMatchParentChildren中,后面重新测量
           //DecorView不会走这个逻辑,因为进过了DecorView的onMeasure()流程,MeasureSpec一定都为EXACTLY
          //会走到下面流程的情况举例:用户自布局一个FrameLayout属性为WRAP_CONTENT是,但子布局为MATCH_PARENT
            if (measureMatchParentChildren) {
                if (lp.width == LayoutParams.MATCH_PARENT ||
                        lp.height == LayoutParams.MATCH_PARENT) {
                    mMatchParentChildren.add(child);
                }
            }
        }
    }
    //最后计算得到的maxWidth和maxHeight的值需要保证能够容纳下当前Layout下所有子View,所以需要对各类情况进行处理
     //所以有以下的加上Padding值,用户设置的Mini尺寸值的对比,设置了背景图片情况的图片大小对比
    // Account for padding too
    maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
    maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();

    // Check against our minimum height and width
    maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
    maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

    // Check against our foreground's minimum height and width
    final Drawable drawable = getForeground();
    if (drawable != null) {
        maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
        maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
    }

    setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
            resolveSizeAndState(maxHeight, heightMeasureSpec,
                    childState << MEASURED_HEIGHT_STATE_SHIFT));

    count = mMatchParentChildren.size();
    // 这里有值表明了两点:
    // 1 当前FrameLayout的宽和高的建议规格有不是精确值的
    // 2 子view有含有match_parent的地方
    if (count > 1) {
        ...
    }
}

FrameLayout的测量很简单,首先遍历所有子View,然后调用measureChildWithMargins进行子View的测量,这个方法是在ViewGroup中的,FrameLayout也是继承自ViewGroup。后面就是相关FrameLayout的测量了

那接着看ViewGroup的measureChildWithMargins方法

protected void measureChildWithMargins(View child,
        int parentWidthMeasureSpec, int widthUsed,
        int parentHeightMeasureSpec, int heightUsed) {
    final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

    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);

    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

这是ViewGroup测量子View提供方法的一种,另一个是measureChildren,区别就是有没有Margins,在这里获取子View的MeasureSpec,然后调用子View自身的measure方法进行测量

取出子View的LayoutParams,然后通过getChildMeasureSpec创建子View的MeasureSpec,接着直接传递给View进行测量;在获取子View的MeasureSepc时,是通过父布局的MeasureSpec和子View本身的LayoutParams共同来决定的,这点可以直接从代码中直到

先看看getChildMeasureSpec方法

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
    //获取父容器的SpecMode和SpecSize
    int specMode = MeasureSpec.getMode(spec);
    int specSize = MeasureSpec.getSize(spec);
    //父容器减去已经占用的空间,就是子View可用的空间大小
    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本身的LayoutParams来确定子元素的MeasureSpec,padding指父容器中已占用的空间大小,因此子View可用的大小是父容器减去paddingint size = Math.max(0, specSize - padding);

普通View的MeasureSpec的创建规则:

childLayoutParams\parentSpecMode EXACTLY AT_MOST UNSPECIFIED
dp/px EXACTLY(childSize) EXACTLY(childSize) EXACTLY(childSize)
match_parent EXACTLY(parentSize) AT_MOST(parentSize) UNSPECIFIED(0)
wrap_content AT_MOST(parentSize) AT_MOST(parentSize) UNSOECIFIED(0)

回到之前,通过父容器的MeasureSpec和自身的LayoutParams确定了子View的MeasureSpec,那接着就会调用子View自己的measure方法

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    ...

    if (forceLayout || needsLayout) {
        ...
        if (cacheIndex < 0 || sIgnoreMeasureCache) {
            // measure ourselves, this should set the measured dimension flag back
            onMeasure(widthMeasureSpec, heightMeasureSpec);
            mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
        } else {
            long value = mMeasureCache.valueAt(cacheIndex);
            // Casting a long to int drops the high 32 bits, no mask needed
            setMeasuredDimensionRaw((int) (value >> 32), (int) value);
            mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
        }
        ...
    }
    ...
}

measure方法是一个final方法,就意味着子类不能重写,在View的measure方法中,会去调用onMeasure方法

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

setMeasuredDimension方法会设置View的宽、高的测量值,所以我们先看getDefaultSize方法

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;
    case MeasureSpec.AT_MOST:
    case MeasureSpec.EXACTLY:
        result = specSize;
        break;
    }
    return result;
}

对于我们来说,只需要看AT_MOST和EXACTLY两种情况,实际上这个方法返回的大小就是measureSpec中的specSize,而这个specSize就是View测量后的大小,而最终的大小是由layout确定的(尽管这样,几乎所有情况,测量大小和最终大小是相等的)。

UNSPECIFIED这种情况一般用于系统内部的测量过程,这种情况下,View的大小为getDefaultSize的第一个参数size,即getSuggestedMinimumWidth、getSuggestedMinimumHeight

 protected int getSuggestedMinimumWidth() {
     return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
 }

 protected int getSuggestedMinimumHeight() {
      return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());
  }

如果View没有设置背景,那么View的宽度、高度为mMinWidth、mMinHeight,这两个对应android:minWidthandroid:minHeight这个两个属性指定的值,如果不指定默认为0;如果设置了背景,则为max(mMinWidth, mBackground.getMinimumWidth());

 public int getMinimumWidth() {
     final int intrinsicWidth = getIntrinsicWidth();
     return intrinsicWidth > 0 ? intrinsicWidth : 0;
 }

 public int getMinimumHeight() {
     final int intrinsicHeight = getIntrinsicHeight();
     return intrinsicHeight > 0 ? intrinsicHeight : 0;
 }

getMinimumWidth返回的就是Drawable的原始宽度、高度,如果Drawable没有原始值,则为0

protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
    boolean optical = isLayoutModeOptical(this);
    if (optical != isLayoutModeOptical(mParent)) {
        Insets insets = getOpticalInsets();
        int opticalWidth  = insets.left + insets.right;
        int opticalHeight = insets.top  + insets.bottom;

        measuredWidth  += optical ? opticalWidth  : -opticalWidth;
        measuredHeight += optical ? opticalHeight : -opticalHeight;
    }
    setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}

最后将测量的宽高设置,子View的测量就结束了

同样,我们看看ViewGroup中单独提供了一个measureChildren方法

protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
    final int size = mChildrenCount;
    final View[] children = mChildren;
    for (int i = 0; i < size; ++i) {
        final View child = children[i];
        if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
        }
    }
}

通过循环遍历所有的子View,然后调用measureChild方法

protected void measureChild(View child, int parentWidthMeasureSpec,
        int parentHeightMeasureSpec) {
    final LayoutParams lp = child.getLayoutParams();

    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
            mPaddingLeft + mPaddingRight, lp.width);
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
            mPaddingTop + mPaddingBottom, lp.height);
    //调用自身的measure方法,普通View都是通过View的measure方法进行测量
    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

最后都是在View中进行自身的测量(对于普通View来说)

layout

ViewGroup的layout

layout过程是从ViewRootImpl中的performLayout(lp, mWidth, mHeight);开始的

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
        int desiredWindowHeight) {
    mLayoutRequested = false;
    mScrollMayChange = true;
    mInLayout = true;

    final View host = mView;
    if (host == null) {
        return;
    }
    if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
        Log.v(mTag, "Laying out " + host + " to (" +
                host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
    }

    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
    try {
        //对自身进行layout
        host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

        mInLayout = false;
        int numViewsRequestingLayout = mLayoutRequesters.size();
        if (numViewsRequestingLayout > 0) {
            // requestLayout() was called during layout.
            // If no layout-request flags are set on the requesting views, there is no problem.
            // If some requests are still pending, then we need to clear those flags and do
            // a full request/measure/layout pass to handle this situation.
            ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,
                    false);
            if (validLayoutRequesters != null) {
                // Set this flag to indicate that any further requests are happening during
                // the second pass, which may result in posting those requests to the next
                // frame instead
                mHandlingLayoutInLayoutRequest = true;

                // Process fresh layout requests, then measure and layout
                int numValidRequests = validLayoutRequesters.size();
                for (int i = 0; i < numValidRequests; ++i) {
                    final View view = validLayoutRequesters.get(i);
                    Log.w("View", "requestLayout() improperly called by " + view +
                            " during layout: running second layout pass");
                    view.requestLayout();
                }
                measureHierarchy(host, lp, mView.getContext().getResources(),
                        desiredWindowWidth, desiredWindowHeight);
                mInLayout = true;
                host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

                mHandlingLayoutInLayoutRequest = false;

                // Check the valid requests again, this time without checking/clearing the
                // layout flags, since requests happening during the second pass get noop'd
                validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);
                if (validLayoutRequesters != null) {
                    final ArrayList<View> finalRequesters = validLayoutRequesters;
                    // Post second-pass requests to the next frame
                    getRunQueue().post(new Runnable() {
                        @Override
                        public void run() {
                            int numValidRequests = finalRequesters.size();
                            for (int i = 0; i < numValidRequests; ++i) {
                                final View view = finalRequesters.get(i);
                                Log.w("View", "requestLayout() improperly called by " + view +
                                        " during second layout pass: posting in next frame");
                                view.requestLayout();
                            }
                        }
                    });
                }
            }

        }
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
    mInLayout = false;
}

在这个方法中,通过host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());来对自身进行layout,host是mView,在这里也就是DecorView。getMeasuredWidth()getMeasuredHeight()获取的是前面Measure后的值,而这个layout方法就是View中的layout方法了

public void layout(int l, int t, int r, int b) {
    if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
        onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
        mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
    }

    int oldL = mLeft;
    int oldT = mTop;
    int oldB = mBottom;
    int oldR = mRight;

    boolean changed = isLayoutModeOptical(mParent) ?
            setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

    if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
        onLayout(changed, l, t, r, b);

        if (shouldDrawRoundScrollbar()) {
            if(mRoundScrollbarRenderer == null) {
                mRoundScrollbarRenderer = new RoundScrollbarRenderer(this);
            }
        } else {
            mRoundScrollbarRenderer = null;
        }

        mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
        //可以通过ListenerInfo中的OnLayoutChangeListener来获取Layout修改的值
        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnLayoutChangeListeners != null) {
            ArrayList<OnLayoutChangeListener> listenersCopy =
                    (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
            int numListeners = listenersCopy.size();
            for (int i = 0; i < numListeners; ++i) {
                listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
            }
        }
    }

    mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
    mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;

    if ((mPrivateFlags3 & PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT) != 0) {
        mPrivateFlags3 &= ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
        notifyEnterOrExitForAutoFillIfNeeded(true);
    }
}

首先会通过setFrame方法来设定View的四个顶点的位置,即初始化mLeft、mRight、mTop、mBottom四个值(**需要注意的是:**这些值都是针对于父View来说的),View四个顶点确定了,那么在父容器的位置也就确定了,接着会调用onLayout方法,这个方法是父容器确定子View的位置(类似onMeasure),onLayout方法是一个空的方法,说明最后都是View自己实现的

那么我们这里是DecorView,传进的参数是host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());,host.getMeasuredWidth()和host.getMeasuredHeight()获取的宽高就是整个屏幕的宽高,那么也就意味着我们的DecorView就是占满了整个屏幕空间

子View的layout(普通View)

DecorView是一个ViewGroup,它的子View的layout过程会在onLayout方法中开始

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    getOutsets(mOutsets);
    //当有偏移的时候就调整
    if (mOutsets.left > 0) {
        offsetLeftAndRight(-mOutsets.left);
    }
    if (mOutsets.top > 0) {
        offsetTopAndBottom(-mOutsets.top);
    }
    if (mApplyFloatingVerticalInsets) {
        offsetTopAndBottom(mFloatingInsets.top);
    }
    if (mApplyFloatingHorizontalInsets) {
        offsetLeftAndRight(mFloatingInsets.left);
    }

    // If the application changed its SystemUI metrics, we might also have to adapt
    // our shadow elevation.
    updateElevation();
    mAllowUpdateElevation = true;

    if (changed && mResizeMode == RESIZE_MODE_DOCKED_DIVIDER) {
        getViewRootImpl().requestInvalidateRootRenderNode();
    }
}

DecorView中的onLayout方法并没有做太多的事情,但是在方法中的第一行就调用了父类的onLayout方法。我们知道DecorView是继承自FrameLayout的,所以布局是在FrameLayout中完成的,接着看看FrameLayout的onLayout方法

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}

在FrameLayout的onLayout方法直接调用了layoutChildren方法

void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
    //获取子View的数量
    final int count = getChildCount();
    //获取各个方向的padding值
    final int parentLeft = getPaddingLeftWithForeground();
    final int parentRight = right - left - getPaddingRightWithForeground();

    final int parentTop = getPaddingTopWithForeground();
    final int parentBottom = bottom - top - getPaddingBottomWithForeground();
    //遍历所有的子View
    for (int i = 0; i < count; i++) {
        final View child = getChildAt(i);
        if (child.getVisibility() != GONE) {
            //获取LayoutParams
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            //获取测量后的宽高
            final int width = child.getMeasuredWidth();
            final int height = child.getMeasuredHeight();

            int childLeft;
            int childTop;

            int gravity = lp.gravity;
            if (gravity == -1) {
                gravity = DEFAULT_CHILD_GRAVITY;
            }
            // 获取布局方向,可能的值为rtl、ltr,分别为从右至左、从左至右布局
            final int layoutDirection = getLayoutDirection();
            // 结合layoutDirection和View的layout_gravity计算出真正的gravity
            // Gravity.getAbsoluteGravity是一个静态方法
            final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
            // 水平方向如果为从左到右,那么,Gratity为START会被设置成LEFT,END会被设置成RIGHT
            // 水平方向如果为从右到左,那么,Gratity为START会被设置成RIGHT,END会被设置成LEFT
            final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
            // 根据不同的水平比重计算View的水平坐标
            switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                case Gravity.CENTER_HORIZONTAL:
                    childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
                    lp.leftMargin - lp.rightMargin;
                    break;
                case Gravity.RIGHT:
                    if (!forceLeftGravity) {
                        childLeft = parentRight - width - lp.rightMargin;
                        break;
                    }
                case Gravity.LEFT:
                default:
                    childLeft = parentLeft + lp.leftMargin;
            }
            //根据垂直比重计算View的垂直坐标
            switch (verticalGravity) {
                case Gravity.TOP:
                    childTop = parentTop + lp.topMargin;
                    break;
                case Gravity.CENTER_VERTICAL:
                    childTop = parentTop + (parentBottom - parentTop - height) / 2 +
                    lp.topMargin - lp.bottomMargin;
                    break;
                case Gravity.BOTTOM:
                    childTop = parentBottom - height - lp.bottomMargin;
                    break;
                default:
                    childTop = parentTop + lp.topMargin;
            }
            //调用子View的layout方法进行子View的layout
            child.layout(childLeft, childTop, childLeft + width, childTop + height);
        }
    }
}

很明显,这个方法会遍历所有的子View,计算出它的位置,最后调用子View自身的layout进行layout过程

View的layout方法

public void layout(int l, int t, int r, int b) {
    if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
        onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
        mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
    }

    int oldL = mLeft;
    int oldT = mTop;
    int oldB = mBottom;
    int oldR = mRight;

    boolean changed = isLayoutModeOptical(mParent) ?
            setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

    if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
        onLayout(changed, l, t, r, b);

        ...
    }

    mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
    mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;

    if ((mPrivateFlags3 & PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT) != 0) {
        mPrivateFlags3 &= ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
        notifyEnterOrExitForAutoFillIfNeeded(true);
    }
}

在View自身的layout过程中,最终就调用其onLayout方法(又是一个递归的过程)

draw

ViewGroup的绘制

从ViewRootImpl的performDraw方法开始

private void performDraw() {
    ...
    final boolean fullRedrawNeeded = mFullRedrawNeeded;
    mFullRedrawNeeded = false;

    mIsDrawing = true;
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
    try {
        draw(fullRedrawNeeded);
    } finally {
        mIsDrawing = false;
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
    ...
}

这个方法中的重点就是draw方法

private void draw(boolean fullRedrawNeeded) {
    Surface surface = mSurface;
    if (!surface.isValid()) {
        return;
    }

    if (DEBUG_FPS) {
        trackFPS();
    }
    //如果是第一次绘制,则会回调到sFirstDrawHandlers中的事件
    //在ActivityThread.attch()方法中有将回调事件加入该队列
    //回调时会执行ActivityThread.ensureJitEnable来确保即时编译相关功能
    if (!sFirstDrawComplete) {
        synchronized (sFirstDrawHandlers) {
            sFirstDrawComplete = true;
            final int count = sFirstDrawHandlers.size();
            for (int i = 0; i< count; i++) {
                mHandler.post(sFirstDrawHandlers.get(i));
            }
        }
    }
    //滚动相关处理,如果scroll发生改变,则回调dispatchOnScrollChanged()方法
    scrollToRectOrFocus(null, false);

    if (mAttachInfo.mViewScrollChanged) {
        mAttachInfo.mViewScrollChanged = false;
        mAttachInfo.mTreeObserver.dispatchOnScrollChanged();
    }
    //窗口当前是否有动画需要执行
    boolean animating = mScroller != null && mScroller.computeScrollOffset();
    ...
    final Rect dirty = mDirty;
    ...

    if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
        //mAttachInfo.mHardwareRenderer不为null,则表示该Window使用硬件加速进行绘制
        //执行ViewRootImpl.set()方法会判断是否使用硬件加速
        //若判断使用会调用ViewRootImpl.enableHardwareAcceleration()来初始化mHardwareRenderer
       //该View设置为使用硬件加速,且当前硬件加速处于可用状态
        if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
            ...
            //使用硬件加速绘制方式
            mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
        } else {
            // If we get here with a disabled & requested hardware renderer, something went
            // wrong (an invalidate posted right before we destroyed the hardware surface
            // for instance) so we should just bail out. Locking the surface with software
            // rendering at this point would lock it forever and prevent hardware renderer
            // from doing its job when it comes back.
            // Before we request a new frame we must however attempt to reinitiliaze the
            // hardware renderer if it's in requested state. This would happen after an
            // eglTerminate() for instance.
            if (mAttachInfo.mThreadedRenderer != null &&
                    !mAttachInfo.mThreadedRenderer.isEnabled() &&
                    mAttachInfo.mThreadedRenderer.isRequested()) {

                try {
                    //尝试重新初始化当前window的硬件加速
                    mAttachInfo.mThreadedRenderer.initializeIfNeeded(
                            mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
                } catch (OutOfResourcesException e) {
                    handleOutOfResourcesException(e);
                    return;
                }

                mFullRedrawNeeded = true;
                scheduleTraversals();
                return;
            }
            //使用软件渲染绘制方式
            if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
                return;
            }
        }
    }
    //有动画进行重绘
    if (animating) {
        mFullRedrawNeeded = true;
        scheduleTraversals();
    }
}

mDirty表示的是当前需要更新的区域,即脏区域。经过一些scroll相关的处理后,如果脏区域不为空或者有动画需要执行时,便会执行重绘窗口的工作。有两种绘制方式,硬件加速绘制方式和软件渲染绘制方式,在创建窗口流程的ViewRootImpl.setView()中,会根据不同情况,来选择是否创mAttachInfo.mHardwareRenderer对象。如果该对象不为空,则会进入硬件加速绘制方式,即调用到ThreadedRenderer.draw(),这个ThreadedRenderer是不会block住UI线程,但是UI线程可以block住它,主要是在UI线程构建试图然后交给单独的线程通过OpenGL去绘制;否则则会进入软件渲染的绘制方式,调用到ViewRootImpl.drawSoftware()方法。但是无论哪种方式,都会走到mView.draw()方法,即DecorView.draw()方法。

主要看看drawSoftWare方法吧

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
        boolean scalingRequired, Rect dirty) {

    // Draw with software renderer.
    final Canvas canvas;
    try {
        final int left = dirty.left;
        final int top = dirty.top;
        final int right = dirty.right;
        final int bottom = dirty.bottom;
        //获取canvas
        canvas = mSurface.lockCanvas(dirty);

        ...
    } catch (Surface.OutOfResourcesException e) {
        handleOutOfResourcesException(e);
        return false;
    } catch (IllegalArgumentException e) {
        ...
    }

    try {
        ...
        try {
            ...

            mView.draw(canvas);

            drawAccessibilityFocusedDrawableIfNeeded(canvas);
        } finally {
            ...
        }
    } finally {
        ...
    }
    return true;
}

在这个方法中,会获取canvas,然后进行一系列的操作,最终会调用mView的draw方法

在这里mView就是DecorView,实际上就是View中的draw方法

public void draw(Canvas canvas) {
    final int privateFlags = mPrivateFlags;
    final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
            (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
    mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

    /*
     * 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)
     */

    // Step 1, draw the background, if needed
    int saveCount;

    if (!dirtyOpaque) {
        //绘制背景
        drawBackground(canvas);
    }

    // skip step 2 & 5 if possible (common case)
    //判断是否需要绘制边缘渐变效果(水平方向、垂直方向)
    final int viewFlags = mViewFlags;
    boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
    boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
    //如果不需要绘制边缘渐变效果,跳过了step5
    if (!verticalEdges && !horizontalEdges) {
        // Step 3, draw the content
        //绘制自己View的内容
        if (!dirtyOpaque) onDraw(canvas);

        // Step 4, draw the children
        //通过dispatchDraw来绘制子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)
        onDrawForeground(canvas);

        // Step 7, draw the default focus highlight
        drawDefaultFocusHighlight(canvas);

        if (debugDraw()) {
            debugDrawFocus(canvas);
        }

        // we're done...
        return;
    }

    /*
     * Here we do the full fledged routine...
     * (this is an uncommon case where speed matters less,
     * this is why we repeat some of the tests that have been
     * done above)
     */

    boolean drawTop = false;
    boolean drawBottom = false;
    boolean drawLeft = false;
    boolean drawRight = false;

    float topFadeStrength = 0.0f;
    float bottomFadeStrength = 0.0f;
    float leftFadeStrength = 0.0f;
    float rightFadeStrength = 0.0f;

    // Step 2, save the canvas' layers
    int paddingLeft = mPaddingLeft;

    final boolean offsetRequired = isPaddingOffsetRequired();
    if (offsetRequired) {
        paddingLeft += getLeftPaddingOffset();
    }

    int left = mScrollX + paddingLeft;
    int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
    int top = mScrollY + getFadeTop(offsetRequired);
    int bottom = top + getFadeHeight(offsetRequired);

    if (offsetRequired) {
        right += getRightPaddingOffset();
        bottom += getBottomPaddingOffset();
    }

    final ScrollabilityCache scrollabilityCache = mScrollCache;
    final float fadeHeight = scrollabilityCache.fadingEdgeLength;
    int length = (int) fadeHeight;

    // clip the fade length if top and bottom fades overlap
    // overlapping fades produce odd-looking artifacts
    if (verticalEdges && (top + length > bottom - length)) {
        length = (bottom - top) / 2;
    }

    // also clip horizontal fades if necessary
    if (horizontalEdges && (left + length > right - length)) {
        length = (right - left) / 2;
    }

    if (verticalEdges) {
        topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
        drawTop = topFadeStrength * fadeHeight > 1.0f;
        bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
        drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
    }

    if (horizontalEdges) {
        leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
        drawLeft = leftFadeStrength * fadeHeight > 1.0f;
        rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
        drawRight = rightFadeStrength * fadeHeight > 1.0f;
    }

    saveCount = canvas.getSaveCount();

    int solidColor = getSolidColor();
    if (solidColor == 0) {
        final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;

        if (drawTop) {
            canvas.saveLayer(left, top, right, top + length, null, flags);
        }

        if (drawBottom) {
            canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
        }

        if (drawLeft) {
            canvas.saveLayer(left, top, left + length, bottom, null, flags);
        }

        if (drawRight) {
            canvas.saveLayer(right - length, top, right, bottom, null, flags);
        }
    } else {
        scrollabilityCache.setFadeColor(solidColor);
    }

    // Step 3, draw the content
    if (!dirtyOpaque) onDraw(canvas);

    // Step 4, draw the children
    dispatchDraw(canvas);

    // Step 5, draw the fade effect and restore layers
    final Paint p = scrollabilityCache.paint;
    final Matrix matrix = scrollabilityCache.matrix;
    final Shader fade = scrollabilityCache.shader;

    if (drawTop) {
        matrix.setScale(1, fadeHeight * topFadeStrength);
        matrix.postTranslate(left, top);
        fade.setLocalMatrix(matrix);
        p.setShader(fade);
        canvas.drawRect(left, top, right, top + length, p);
    }

    if (drawBottom) {
        matrix.setScale(1, fadeHeight * bottomFadeStrength);
        matrix.postRotate(180);
        matrix.postTranslate(left, bottom);
        fade.setLocalMatrix(matrix);
        p.setShader(fade);
        canvas.drawRect(left, bottom - length, right, bottom, p);
    }

    if (drawLeft) {
        matrix.setScale(1, fadeHeight * leftFadeStrength);
        matrix.postRotate(-90);
        matrix.postTranslate(left, top);
        fade.setLocalMatrix(matrix);
        p.setShader(fade);
        canvas.drawRect(left, top, left + length, bottom, p);
    }

    if (drawRight) {
        matrix.setScale(1, fadeHeight * rightFadeStrength);
        matrix.postRotate(90);
        matrix.postTranslate(right, top);
        fade.setLocalMatrix(matrix);
        p.setShader(fade);
        canvas.drawRect(right - length, top, right, bottom, p);
    }

    canvas.restoreToCount(saveCount);

    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)
    onDrawForeground(canvas);

    if (debugDraw()) {
        debugDrawFocus(canvas);
    }
}

在draw方法中,根据上面注释可以知道draw的过程分为5步

  1. 画出背景background
  2. 判断是否需要画边缘的渐变效果
  3. 画出当前View需要显示的内容,调用onDraw()来实现
  4. 调用dispatchDraw()方法,进入子视图的draw逻辑
  5. 如果需要花边缘渐变效果,则在这里画
  6. 绘制装饰(如滚动条)

在绘制自己的时候,是调用onDraw方法,这个方法是个空实现,是由具体的View自己实现的,我们这里是DecorView,那么就会回到DecorView的onDraw方法

@Override
public void onDraw(Canvas c) {
    super.onDraw(c);

    // When we are resizing, we need the fallback background to cover the area where we have our
    // system bar background views as the navigation bar will be hidden during resizing.
    mBackgroundFallback.draw(isResizing() ? this : mContentRoot, mContentRoot, c,
            mWindow.mContentParent);
}

子View的绘制

View绘制过程的传递时通过dispatchDraw来实现的,dispatchDraw会遍历所有子元素的draw方法,这样draw事件就一层层的传递下去

dispatchDraw方法是在ViewGroup中实现的

@Override
protected void dispatchDraw(Canvas canvas) {
    boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
    final int childrenCount = mChildrenCount;
    final View[] children = mChildren;
    int flags = mGroupFlags;
    ...
    //此处不分析具体流程,需要了解的可查看相关博客
    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) {
                //遍历所有子View进行子View的绘制
                more |= drawChild(canvas, transientChild, drawingTime);
            }
            transientIndex++;
            if (transientIndex >= transientCount) {
                transientIndex = -1;
            }
        }

        final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
        final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
        if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
            more |= drawChild(canvas, child, drawingTime);
        }
    }
    ...
}

在dispatch中会遍历当前ViewGroup的子视图,然后调用drawChild()方法来依次调起子视图的绘制过程,进入ViewGroup.drawChild()代码

protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
    return child.draw(canvas, this, drawingTime);
}

好了,这就又回到了View中的draw方法了

注意
View中有一个特殊的方法setWillNotDraw方法

/**
 * If this view doesn't do any drawing on its own, set this flag to
 * allow further optimizations. By default, this flag is not set on
 * View, but could be set on some View subclasses such as ViewGroup.
 *
 * Typically, if you override {@link #onDraw(android.graphics.Canvas)}
 * you should clear this flag.
 *
 * @param willNotDraw whether or not this View draw on its own
 */
public void setWillNotDraw(boolean willNotDraw) {
    setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
}

从注释中可以看出,如果一个View不需要绘制任何内容,那个设置这个标记为true后,系统会进行相应的优化。默认情况下View没有启动这个优化标记位,但是ViewGroup会默认启动这个优化标记位。

总结

Measure总结

顶层View向子View递归调用View.measure()方法,measure中又会回调onMeasure()方法

  • MeasureSpec:测量模式和size的一个组合体,其中SpecMode只有三种

    MeasureSpec.EXACTLY::父容器已经检测出View所需要的精确大小,这个时候View的最终大小就是SpecSize指定的值,对应于LayoutParams中的match_parent和具体的数值这两种模式

    MeasureSpec.AT_MOST:父容器指定了一个可用大小的SpecSize,View的大小不能大于这个值,具体是什么看不同View的具体实现。对应于LayoutParams中的wrap_content

    MeasureSpec.UNSPECIFIED:父容器对View不会有任何限制,要多大给多大,一般用于系统内部,表示一种测量状态

  • View的measure方法是final的,View子类只能在onMeasure方法中完成自己测量逻辑

  • 最顶层DecorView测量时的MeasureSpec是由ViewRootImpl中getRootMeasureSpec方法确定的(LayoutParams宽高参数均为MATCH_PARENT,specMode是EXACTLY,specSize为物理屏幕大小)

  • ViewGroup类提供了measureChild,measureChild和measureChildWithMargins方法,简化了父子View的尺寸计算

  • 只要是ViewGroup的子类就必须要求LayoutParams继承子MarginLayoutParams,否则无法使用layout_margin参数

  • View布局的大小由父布局和子view共同决定(父布局的MeasureSpec和子View的LayoutParams)

Layout总结

类似Measure,都是从顶层View向子View递归调用View.layoutt()方法,在layout中又会回调onLayout()方法

  • View.layout方法可被重载,ViewGroup.layout为final的不可重载,ViewGroup.onLayout为abstract的,子类必须重载实现自己的位置逻辑
  • measure操作完成后得到的是对每个View经测量过的measuredWidth和measuredHeight,layout操作完成之后得到的是对每个View进行位置分配后的mLeft、mTop、mRight、mBottom,这些值都是相对于父View来说的
  • 使用View的getWidth()和getHeight()方法来获取View测量的宽高,必须保证这两个方法在onLayout流程之后被调用才能返回有效值

Draw总结

  • 6个步骤:
    1. 画出背景background
    2. 判断是否需要画边缘的渐变效果
    3. 画出当前View需要显示的内容,调用onDraw()来实现
    4. 调用dispatchDraw()方法,进入子视图的draw逻辑
    5. 如果需要花边缘渐变效果,则在这里画
    6. 绘制装饰(如滚动条)
  • View默认不会绘制任何内容,真正的绘制都需要自己在子类中实现。
  • View默认不会绘制任何内容,真正的绘制都需要自己在子类中实现
  • 默认情况下子View的ViewGroup.drawChild绘制顺序和子View被添加的顺序一致,但是你也可以重载ViewGroup.getChildDrawingOrder()方法提供不同顺序

特别感谢

Android开发艺术探索

源码分析篇 - Android绘制流程(二)measure、layout、draw流程
Android应用层View绘制流程与源码分析

FrameLayout布局绘制流程解析

View测量机制详解—从DecorView说起

发布了43 篇原创文章 · 获赞 10 · 访问量 6998

猜你喜欢

转载自blog.csdn.net/baidu_36959886/article/details/83065604