在 Android 的知识体系中,View 扮演者很重要的角色。View 是 Android 在视觉上的呈现。本文结合 android-28 的源码来分析 View 的绘制过程。

ViewRootImpl

ViewRootImpl 类是连接 WindowManagerDecorView 的纽带,View 的绘制流程均是通过 ViewRootImpl 来完成的。

// ActivityThread.java
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">handleResumeActivity</span><span class="hljs-params">(IBinder token, <span class="hljs-keyword">boolean</span> finalStateRequest, <span class="hljs-keyword">boolean</span> isForward,
        String reason)</span> </span>{
    
    ...
    
    <span class="hljs-keyword">if</span> (r.window == <span class="hljs-keyword">null</span> &amp;&amp; !a.mFinished &amp;&amp; willBeVisible) {
        <span class="hljs-comment">// 获取 WindowManager 及 DecorView</span>
        r.window = r.activity.getWindow();
        View decor = r.window.getDecorView();
        decor.setVisibility(View.INVISIBLE);
        ViewManager wm = a.getWindowManager();
        WindowManager.LayoutParams l = r.window.getAttributes();
        a.mDecor = decor;
        l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
        l.softInputMode |= forwardBit;
        <span class="hljs-keyword">if</span> (r.mPreserveWindow) {
            a.mWindowAdded = <span class="hljs-keyword">true</span>;
            r.mPreserveWindow = <span class="hljs-keyword">false</span>;
            <span class="hljs-comment">// Normally the ViewRoot sets up callbacks with the Activity</span>
            <span class="hljs-comment">// in addView-&gt;ViewRootImpl#setView. If we are instead reusing</span>
            <span class="hljs-comment">// the decor view we have to notify the view root that the</span>
            <span class="hljs-comment">// callbacks may have changed.</span>
            ViewRootImpl impl = decor.getViewRootImpl();
            <span class="hljs-keyword">if</span> (impl != <span class="hljs-keyword">null</span>) {
                impl.notifyChildRebuilt();
            }
        }
        <span class="hljs-keyword">if</span> (a.mVisibleFromClient) {
            <span class="hljs-keyword">if</span> (!a.mWindowAdded) {
                a.mWindowAdded = <span class="hljs-keyword">true</span>;
                <span class="hljs-comment">// 将 DecorView 添加到当前 Window 中</span>
                wm.addView(decor, l);
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-comment">// The activity will get a callback for this {@link LayoutParams} change</span>
                <span class="hljs-comment">// earlier. However, at that time the decor will not be set (this is set</span>
                <span class="hljs-comment">// in this method), so no action will be taken. This call ensures the</span>
                <span class="hljs-comment">// callback occurs with the decor set.</span>
                a.onWindowAttributesChanged(l);
            }
        }

        <span class="hljs-comment">// If the window has already been added, but during resume</span>
        <span class="hljs-comment">// we started another activity, then don't yet make the</span>
        <span class="hljs-comment">// window visible.</span>
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (!willBeVisible) {
        <span class="hljs-keyword">if</span> (localLOGV) Slog.v(TAG, <span class="hljs-string">"Launch "</span> + r + <span class="hljs-string">" mStartedActivity set"</span>);
        r.hideForNow = <span class="hljs-keyword">true</span>;
    }

    ...
}
复制代码

上面的代码说明,在 ActivityThread 中,当 Activity 对象被创建完毕后,会将 DecorView 通过 WindowManager 添加到 Window 中。

// WindowManagerImpl.java
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">addView</span><span class="hljs-params">(@NonNull View view, @NonNull ViewGroup.LayoutParams params)</span> </span>{
    applyDefaultToken(params);
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
复制代码

可以知道最终是通过 WindowManagerGlobaladdView 方法来将 DecorView 添加到 Window

// WindowManagerGlobal
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">addView</span><span class="hljs-params">(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow)</span> </span>{
   ...

    ViewRootImpl root;
    View panelParentView = <span class="hljs-keyword">null</span>;

    <span class="hljs-keyword">synchronized</span> (mLock) {
        ...

        <span class="hljs-comment">// 初始化 ViewRootImpl 并将 ViewRootImpl 对象和  DecorView 建立关联</span>
        root = <span class="hljs-keyword">new</span> ViewRootImpl(view.getContext(), display);
        view.setLayoutParams(wparams);

        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);

        <span class="hljs-comment">// do this last because it fires off messages to start doing things</span>
        <span class="hljs-keyword">try</span> {
            root.setView(view, wparams, panelParentView);
        } <span class="hljs-keyword">catch</span> (RuntimeException e) {
            <span class="hljs-comment">// BadTokenException or InvalidDisplayException, clean up.</span>
            <span class="hljs-keyword">if</span> (index &gt;= <span class="hljs-number">0</span>) {
                removeViewLocked(index, <span class="hljs-keyword">true</span>);
            }
            <span class="hljs-keyword">throw</span> e;
        }
    }
}
复制代码

上述代码创建了 ViewRootImpl 对象,并将 ViewRootImpl 对象和 DecorView 建立关联。最终在 setView 方法中,会执行 ViewRootImplrequestLayout 方法来执行 View 的绘制流程

// ViewRootImpl.java
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">requestLayout</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">if</span> (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = <span class="hljs-keyword">true</span>;
        scheduleTraversals();
    }
}
复制代码

scheduleTraversals 方法最终会调用 performTraversals 方法,经过 measurelayoutdraw 三个过程才能最终将一个 View 绘制出来

  • meausre: 用来测量 View 的宽和高
  • layout: 用来确定 View 在父容器中的放置位置
  • draw: 负责将 View 绘制在屏幕上

如图所示,performTraversals 会依次调用 performMeasureperformLayoutperformDraw 三个方法,这三个方法分别完成顶级 Viewmeasurelayoutdraw 这三大流程。其中在 performMeasure 中会调用 measure 方法,在 measure 方法中又会去调用 onMeasure 方法,在 onMeasure 方法中又会对所有的子元素进行 measure 过程,这个时候 measure 流程就从父容器传递到了子元素中了,这样就完成了一次 measure 过程。接着子元素会重复父容器的 measure 过程,如此反复就完成了整个 View 树的遍历。通过 performLayoutperformDraw 的传递流程跟 performMeasure 类似

MeasureSpec

为了更好地理解 View 的测量过程,我们还需要理解 MeasureSpecMeasureSpec 参与了 Viewmeasure 过程,在很大程度上决定了一个 View 的尺寸规格,但父容器也会影响 ViewMeasureSpec 的创建过程。在测量过程中,系统会将 ViewLayoutParams 根据父容器所试驾的规则转换成对应的 MeasureSpec,然后再根据这个 MeasureSpec 来测量出 View 的宽和高。

// View.java
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MeasureSpec</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> MODE_SHIFT = <span class="hljs-number">30</span>;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> MODE_MASK  = <span class="hljs-number">0x3</span> &lt;&lt; MODE_SHIFT;

    <span class="hljs-comment">/** <span class="hljs-doctag">@hide</span> */</span>
    <span class="hljs-meta">@IntDef</span>({UNSPECIFIED, EXACTLY, AT_MOST})
    <span class="hljs-meta">@Retention</span>(RetentionPolicy.SOURCE)
    <span class="hljs-keyword">public</span> <span class="hljs-meta">@interface</span> MeasureSpecMode {}

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> UNSPECIFIED = <span class="hljs-number">0</span> &lt;&lt; MODE_SHIFT;

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> EXACTLY     = <span class="hljs-number">1</span> &lt;&lt; MODE_SHIFT;

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> AT_MOST     = <span class="hljs-number">2</span> &lt;&lt; MODE_SHIFT;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">makeMeasureSpec</span><span class="hljs-params">(@IntRange(from = <span class="hljs-number">0</span>, to = (<span class="hljs-number">1</span> &lt;&lt; MeasureSpec.MODE_SHIFT)</span> - 1) <span class="hljs-keyword">int</span> size,
                                      @MeasureSpecMode <span class="hljs-keyword">int</span> mode) </span>{
        <span class="hljs-keyword">if</span> (sUseBrokenMakeMeasureSpec) {
            <span class="hljs-keyword">return</span> size + mode;
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> (size &amp; ~MODE_MASK) | (mode &amp; MODE_MASK);
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">makeSafeMeasureSpec</span><span class="hljs-params">(<span class="hljs-keyword">int</span> size, <span class="hljs-keyword">int</span> mode)</span> </span>{
        <span class="hljs-keyword">if</span> (sUseZeroUnspecifiedMeasureSpec &amp;&amp; mode == UNSPECIFIED) {
            <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
        }
        <span class="hljs-keyword">return</span> makeMeasureSpec(size, mode);
    }

    <span class="hljs-meta">@MeasureSpecMode</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getMode</span><span class="hljs-params">(<span class="hljs-keyword">int</span> measureSpec)</span> </span>{
        <span class="hljs-comment">//noinspection ResourceType</span>
        <span class="hljs-keyword">return</span> (measureSpec &amp; MODE_MASK);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getSize</span><span class="hljs-params">(<span class="hljs-keyword">int</span> measureSpec)</span> </span>{
        <span class="hljs-keyword">return</span> (measureSpec &amp; ~MODE_MASK);
    }
    
    <span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">adjust</span><span class="hljs-params">(<span class="hljs-keyword">int</span> measureSpec, <span class="hljs-keyword">int</span> delta)</span> </span>{
        <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> mode = getMode(measureSpec);
        <span class="hljs-keyword">int</span> size = getSize(measureSpec);
        <span class="hljs-keyword">if</span> (mode == UNSPECIFIED) {
            <span class="hljs-comment">// No need to adjust size for UNSPECIFIED mode.</span>
            <span class="hljs-keyword">return</span> makeMeasureSpec(size, UNSPECIFIED);
        }
        size += delta;
        <span class="hljs-keyword">if</span> (size &lt; <span class="hljs-number">0</span>) {
            Log.e(VIEW_LOG_TAG, <span class="hljs-string">"MeasureSpec.adjust: new size would be negative! ("</span> + size +
                    <span class="hljs-string">") spec: "</span> + toString(measureSpec) + <span class="hljs-string">" delta: "</span> + delta);
            size = <span class="hljs-number">0</span>;
        }
        <span class="hljs-keyword">return</span> makeMeasureSpec(size, mode);
    }
    
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">toString</span><span class="hljs-params">(<span class="hljs-keyword">int</span> measureSpec)</span> </span>{
        <span class="hljs-keyword">int</span> mode = getMode(measureSpec);
        <span class="hljs-keyword">int</span> size = getSize(measureSpec);

        StringBuilder sb = <span class="hljs-keyword">new</span> StringBuilder(<span class="hljs-string">"MeasureSpec: "</span>);

        <span class="hljs-keyword">if</span> (mode == UNSPECIFIED)
            sb.append(<span class="hljs-string">"UNSPECIFIED "</span>);
        <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (mode == EXACTLY)
            sb.append(<span class="hljs-string">"EXACTLY "</span>);
        <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (mode == AT_MOST)
            sb.append(<span class="hljs-string">"AT_MOST "</span>);
        <span class="hljs-keyword">else</span>
            sb.append(mode).append(<span class="hljs-string">" "</span>);

        sb.append(size);
        <span class="hljs-keyword">return</span> sb.toString();
    }
}
复制代码

MeasureSpec 代表一个 32 位的 int 值,高 2 位代表 SpecMode, 低 30 位代表 SpecSize

  • SpecMode: 测量模式
  • SpecSize: 在某种测量模式下的规格大小

MeasureSpec 通过将 SpecModeSpecSize 打包成一个 int 值来避免过多的对象内存分配makeMeasureSpec 是打包方法,getModegetSize 则为解包方法。

SpecMode 有三类,每一类都标识特殊的含义

UNSPECIFIED

父容器不对 View 有任何限制,要多大给多大,这种情况一般用于系统内部,标识一种测量的状态

EXACTLY

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

AT_MOST

父容器指定了一个可用大小即 SpecSizeView 的大小不能大于这个值,具体是什么值要看不同 View 的具体实现。它对应于 LayoutParams 中的 wrap_content

MeasupreSpec 和 LayoutParams

MeasureSpec 不是唯一由 LayoutParams 决定的,LayoutParams 需要和父容器一起才能决定 ViewMeasureSpec,从而进一步决定 View 的宽和高。

对于 DecorView,其 MeasureSpec 由窗口的尺寸和其自身的 LayoutParams 来共同确定;对于普通的 View,其 MeasureSpec 由父容器的 MeasureSpec 和自身的 LayoutParams 来共同局诶的那个,MeasureSpec 一旦确定后, onMeasure 中就可以确定 View 的测量宽和高

DecorView 创建 MeasureSpec

对于 DecorView 来说,它的 MeasureSpec 创建过程是由 getRootMeasureSpec 方法来完成的

// ViewRootImpl.java
<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getRootMeasureSpec</span><span class="hljs-params">(<span class="hljs-keyword">int</span> windowSize, <span class="hljs-keyword">int</span> rootDimension)</span> </span>{
    <span class="hljs-keyword">int</span> measureSpec;
    <span class="hljs-keyword">switch</span> (rootDimension) {

    <span class="hljs-keyword">case</span> ViewGroup.LayoutParams.MATCH_PARENT: <span class="hljs-comment">// 精确模式,大小就是窗口大小</span>
        <span class="hljs-comment">// Window can't resize. Force root view to be windowSize.</span>
        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
        <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">case</span> ViewGroup.LayoutParams.WRAP_CONTENT: <span class="hljs-comment">// 最大模式,大小不定,但是不能超过窗口的大小</span>
        <span class="hljs-comment">// Window can resize. Set max size for root view.</span>
        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
        <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">default</span>: <span class="hljs-comment">// 精确模式,大小为 LayoutParams 中指定的大小</span>
        <span class="hljs-comment">// Window wants to be an exact size. Force root view to be that size.</span>
        measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
        <span class="hljs-keyword">break</span>;
    }
    <span class="hljs-keyword">return</span> measureSpec;
}
复制代码

通过上述代码,DecorViewMeasureSpec 的产生过程就很明确了,具体来说其遵守如下规则,根据它的 LayoutParams 中的宽和高参数来划分

  • LayoutParams.MATCH_PARENT: 精确模式,大小就是窗口大小
  • LayoutParams.WRAP_CONTENT: 最大模式,大小不定,但是不能超过窗口的大小
  • 固定大小(如 100dp): 精确模式,大小为 LayoutParams 中指定的大小

ViewRootImplperformTraversals 方法中调用 getRootMeasureSpec 获取到 childWidthMeasureSpecchildHeightMeasureSpec 后,会传给 performMeasure 方法,最终调用 DecorViewmeasure 方法

普通 View 创建 MeasureSpec

对于普通 View 来说,即布局中的 ViewViewmeasure 过程由 ViewGroup 传递而来,在 ViewGroupmeasureChildWithMargins 方法

// ViewGroup.java
<span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">measureChildWithMargins</span><span class="hljs-params">(View child,
        <span class="hljs-keyword">int</span> parentWidthMeasureSpec, <span class="hljs-keyword">int</span> widthUsed,
        <span class="hljs-keyword">int</span> parentHeightMeasureSpec, <span class="hljs-keyword">int</span> heightUsed)</span> </span>{
    <span class="hljs-keyword">final</span> MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

    <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
            mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                    + widthUsed, lp.width);
    <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
            mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                    + heightUsed, lp.height);

    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
复制代码

measureChildWithMargins 方法一般会在自定义 Layout 组件的 onMeasure 方法中调用(如 FrameLayout, LinearLayout),来测量子元素的规格。在调用子元素的 measure 方法之前会先通过 getChildMeasureSpec 方法来得到子元素的 MeasureSpec。通过上面代码可知,子元素的 MeasureSpec 的创建和父容器的 MeasureSpec 和子元素本身的 LayoutParams 有关,此外还和 Viewmarginpadding 有关

// ViewGroup.java
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getChildMeasureSpec</span><span class="hljs-params">(<span class="hljs-keyword">int</span> spec, <span class="hljs-keyword">int</span> padding, <span class="hljs-keyword">int</span> childDimension)</span> </span>{
    <span class="hljs-comment">// 父容器的 mode 和 size</span>
    <span class="hljs-keyword">int</span> specMode = MeasureSpec.getMode(spec);
    <span class="hljs-keyword">int</span> specSize = MeasureSpec.getSize(spec);

    <span class="hljs-comment">// 子元素可用大小</span>
    <span class="hljs-keyword">int</span> size = Math.max(<span class="hljs-number">0</span>, specSize - padding);

    <span class="hljs-keyword">int</span> resultSize = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">int</span> resultMode = <span class="hljs-number">0</span>;

    <span class="hljs-keyword">switch</span> (specMode) {
    <span class="hljs-comment">// Parent has imposed an exact size on us</span>
    <span class="hljs-keyword">case</span> MeasureSpec.EXACTLY:
        <span class="hljs-keyword">if</span> (childDimension &gt;= <span class="hljs-number">0</span>) {
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (childDimension == LayoutParams.MATCH_PARENT) {
            <span class="hljs-comment">// Child wants to be our size. So be it.</span>
            resultSize = size;
            resultMode = MeasureSpec.EXACTLY;
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (childDimension == LayoutParams.WRAP_CONTENT) {
            <span class="hljs-comment">// Child wants to determine its own size. It can't be</span>
            <span class="hljs-comment">// bigger than us.</span>
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        <span class="hljs-keyword">break</span>;

    <span class="hljs-comment">// Parent has imposed a maximum size on us</span>
    <span class="hljs-keyword">case</span> MeasureSpec.AT_MOST:
        <span class="hljs-keyword">if</span> (childDimension &gt;= <span class="hljs-number">0</span>) {
            <span class="hljs-comment">// Child wants a specific size... so be it</span>
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (childDimension == LayoutParams.MATCH_PARENT) {
            <span class="hljs-comment">// Child wants to be our size, but our size is not fixed.</span>
            <span class="hljs-comment">// Constrain child to not be bigger than us.</span>
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (childDimension == LayoutParams.WRAP_CONTENT) {
            <span class="hljs-comment">// Child wants to determine its own size. It can't be</span>
            <span class="hljs-comment">// bigger than us.</span>
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        <span class="hljs-keyword">break</span>;

    <span class="hljs-comment">// Parent asked to see how big we want to be</span>
    <span class="hljs-keyword">case</span> MeasureSpec.UNSPECIFIED:
        <span class="hljs-keyword">if</span> (childDimension &gt;= <span class="hljs-number">0</span>) {
            <span class="hljs-comment">// Child wants a specific size... let him have it</span>
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (childDimension == LayoutParams.MATCH_PARENT) {
            <span class="hljs-comment">// Child wants to be our size... find out how big it should</span>
            <span class="hljs-comment">// be</span>
            resultSize = View.sUseZeroUnspecifiedMeasureSpec ? <span class="hljs-number">0</span> : size;
            resultMode = MeasureSpec.UNSPECIFIED;
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (childDimension == LayoutParams.WRAP_CONTENT) {
            <span class="hljs-comment">// Child wants to determine its own size.... find out how</span>
            <span class="hljs-comment">// big it should be</span>
            resultSize = View.sUseZeroUnspecifiedMeasureSpec ? <span class="hljs-number">0</span> : size;
            resultMode = MeasureSpec.UNSPECIFIED;
        }
        <span class="hljs-keyword">break</span>;
    }
    <span class="hljs-comment">//noinspection ResourceType</span>
    <span class="hljs-keyword">return</span> MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
复制代码

getChildMeasureSpec 函数主要的作用是根据父容器的 MeasureSpec 同时结合 View 本身的 LayoutParams 来确定子元素的 MeasureSpec

View 采用固定宽和高的时候,不管父容器的 MeasureSpec 是什么,ViewMeasureSpec 都是精确模式并且其大小遵循 LayoutParamas 中的大小。当 View 的宽和高是 match_parent 时,如果父容器的模式是精确模式,那么 View 也是精确模式并且其大小是父容器的剩余空间;如果父容器是最大模式,那么 View 也是最大模式并且其大小不会超过父容器的剩余空间。如果父容器是最大模式,那么 View 也是最大模式并且其大小不会超过父容器的剩余空间。当 View 的宽和高是 wrap_content 时,不管父容器的模式是精准还是最大化,View 的模式总是最大化并且大小不能超过父容器的剩余空间。

View 工作流程

  • Android

    开放手机联盟(一个由 30 多家科技公司和手机公司组成的团体)已开发出 Android,Android 是第一个完整、开放、免费的手机平台。

    293 引用
感谢    赞同    分享    收藏    关注    反对    举报    ...