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