AndroidUI繪制流程,一步一步深入源碼解析(三)view測量上篇

view的測量

根據(二)中分析,我們知道view的測量、布局、繪制分別調用了performMeasure、performLayout、perfromDraw方法;

源碼如下:


總結UI詳細測量、布局和繪制步驟如下:

1、測量:

View.measure--->View.onMeasure--->View.setMeasuredDimension--->View.setMeasureDimensionRaw

2、布局:

View.layout ---> View.onLayout

3、繪制:

ViewRootImpl.draw(fullRedrawNeeded)--->ViewRootImpl.drawSoftware--->view.draw(Canvas)

下面我們依次分析具體的測量、布局和繪制實現邏輯

測量:

/frameworks/base/core/java/android/view/View.java

View的測量會執行measure(int widthMeasureSpec, int heightMeasureSpec)方法,至于measure怎么執行先不探討,我們先了解一下其中的兩個參數,其實他們都是int類型的MeasureSpec。MeasureSpec可以說是View測量過程的前提,所以我們很有必要先來了解一下MeasureSpec。

MeasureSpec 工作原理

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

SpecMode是指測量模式,SpecSize是指在某種測量模式下的大小。

MeasureSpec是View中的一個靜態內部類。


我們可以把MeasureSpec理解為測量規則,而這個測量規則是由測量模式和和該模式下的測量大小共同組成的。


使用方式如下:

int MeasureSpec = MeasureSpec.makeMeasureSpec(specSize,SpecMode);

確定了View測量規則后,我們也可以通過測量規則獲取測量模式和該模式下的測量大小。

int specMode = MeasureSpec.getMode(measureSpec);

int specSize = MeasureSpec.getSize(measureSpec);

SpecMode有三類:

UNSPECIFIED

父容器不對View有任何限制,要多大給多大,這般情況一般用于系統內部,表示一種測量狀態,如ScrollView測量子View時用的就是這個。

EXACTLY

父容器已經檢測出View所需要的大小,這個時候View的最終大小就是SpecSize所測定的值,它對應于LayoutParams中的match_parent和具體的數值(如40dp,60dp)這兩種模式。

AT_MOST

父容器指定了一個可用大小即SpecSize,View的大小不能大于這個值,具體是什么值要看不同View的具體實現。它對應于LayoutParams中的wrap_content.

下面我們再詳細分解普通View如何測量

普通View的MeasureSpec的創建過程

MeasureSpec很重要,上文中我們也了解了MeasureSpec的工作原理,那如何獲取MeasureSpec呢?下面就結合源碼來分析MeasureSpec的創建過程。

/frameworks/base/core/java/android/view/ViewGroup.java

先來看下ViewGroup中的measureChild方法


在這個方法中,先獲取了子View的布局參數,然后通過getChildMeasureSpec方法分別得到子View的寬高測量規則,即childWidthMeasureSpec和childHeightMeasureSpec,最后調用子View的measure方法,至此測量過程就由父View傳遞到了子View.。MeasureSpec確定后就可以在onMeasure方法確定View的測量寬高了。

下面重點分析一下getChildMeasureSpec,源碼如下:

/**

? ? * 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的LayoutParams來確定子View的MeasureSpec的,即普通View的MeasureSpec創建過程。


接著我們再了解一下DecorView的MeaureSpace是如何創建的

普通View的MeasureSpec的創建過程闡述了怎樣通過父View的MeasureSpec和子View的LayoutParams來確定子View的MeasureSpec。那頂級View,即DecorView的MeasureSpec創建過程又是怎樣的呢?ViewRootImp的measureHierarchy方法中有如下代碼:


接著來看getRootMeasureSpec方法


從上述源碼,我們可以得出如下規則,具體根據它的LayoutParams來劃分:

LayoutParams.MATCH_PARENT:精確模式 其大小就為屏幕的尺寸大小

ViewGroup.LayoutParams.WRAP_CONTENT:最大模式,大小不定,但是不能超過屏幕的大小

具體數值(如40dp):精確模式,大小為LayoutParamas指定的大小。

參考博文:https://www.cnblogs.com/xyhuangjinfu/p/5435201.html

上一篇:AndroidUI繪制流程,一步一步深入源碼解析(二)

下一篇:AndroidUI繪制流程,一步一步深入源碼解析(三)view測量下篇

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容