要理解View的測量,首先要瞭解MeasureSpec,系統在測量view的寬高時,要先確定MeasureSpec。 MeasureSpec(32為int值)由兩部分組成: SpecMode(高2位):測量模式。 SpecSize(低30位):某種測量模式下的規格大小。 SpecMode有3類: U ...
要理解View的測量,首先要瞭解MeasureSpec,系統在測量view的寬高時,要先確定MeasureSpec。
MeasureSpec(32為int值)由兩部分組成:
SpecMode(高2位):測量模式。
SpecSize(低30位):某種測量模式下的規格大小。
SpecMode有3類:
UNSPECIFIED: 父容器不對view做大小限制,一般用於系統內部,表示一種測量狀態。
EXACTLY:精確模式。對應於:LayoutPrams中的match_parent和具體數值。
AT_MOST:最大值模式。對應於LayoutParam中的wrap_content模式。
接下來我們看看View的onMeasure方法:
/** * <p> * Measure the view and its content to determine the measured width and the * measured height. This method is invoked by {@link #measure(int, int)} and * should be overridden by subclasses to provide accurate and efficient * measurement of their contents. * </p> * protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }
該方法是由measure來調用的,view寬高的測量值最終是通過setMeasuredDimension來設置,具體實現不去深究。
我們看看getDefaultSize方法:
/** * Utility to return a default size. Uses the supplied size if the * MeasureSpec imposed no constraints. Will get larger if allowed * by the MeasureSpec. * * @param size Default size for this view * @param measureSpec Constraints imposed by the parent * @return The size this view should be. */ 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; }
先從MeasureSpec中獲取的測量模式specMode和該模式下的大小specSize,然後對測量模式進行判斷,如果是UNSPECIFIED,那麼結果就是參數size,對於這個size是什麼等會討論。如果是AT_MOST和EXACTLY,那麼結果就是view測量後的大小specSize。
那麼這個參數size到底是什麼呢?它是由實參getSuggestedMinimumWidth()和getSuggestedMinimumHeight()傳下來的,我們進去getSuggestedMinimumWidth()看看。
/** * Returns the suggested minimum width that the view should use. This * returns the maximum of the view's minimum width) * and the background's minimum width * ({@link android.graphics.drawable.Drawable#getMinimumWidth()}). * <p> * When being used in {@link #onMeasure(int, int)}, the caller should still * ensure the returned width is within the requirements of the parent. * * @return The suggested minimum width of the view. */ protected int getSuggestedMinimumWidth() { return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth()); }
從代碼我們可以看出如果該view設置了背景,那麼就返回max(mMinWidth, mBackground.getMinimumWidth()),否則返回mMinWidth。
mMinWidth是什麼呢?它是對應android:minWidth這個屬性的值,若是不指定,那麼預設為0.
max(mMinWidth, mBackground.getMinimumWidth()):返回mMinWidth和mBackground.getMinimumWidth()兩者中的最大值。
mBackground.getMinimumWidth()的代碼如下:
/** * Returns the minimum width suggested by this Drawable. If a View uses this * Drawable as a background, it is suggested that the View use at least this * value for its width. (There will be some scenarios where this will not be * possible.) This value should INCLUDE any padding. * * @return The minimum width suggested by this Drawable. If this Drawable * doesn't have a suggested minimum width, 0 is returned. */ public int getMinimumWidth() { final int intrinsicWidth = getIntrinsicWidth(); return intrinsicWidth > 0 ? intrinsicWidth : 0; }
它返回的是Drawable的原始寬度,如果Drawable有原始寬度的話。否則返回0.
因此若view有設置背景,那麼size就是android:minWidth和背景最小寬度這兩者的最大值,若沒有設置背景,那麼size的值就是android:minWidth的值。