View 測量大小

2021-07-10 18:59:40 字數 4185 閱讀 2924

view 的繪製要經過 measure ,layout 和 draw 這三個步驟,這篇記錄下測量時的關鍵點。

* the actual measurement work of a view is performed in

* , called by this method. therefore, only

* can and must be overridden by subclasses.

如注釋所言,直接看 onmeasure 函式就行了。

protected void onmeasure(int widthmeasurespec, int heightmeasurespec)

先得說下這個,measurespec 是乙個 32 位的 int,代表了 view 的 specsize 和 specmode.

specsize 就是 view 測量後的高度或寬度。

specmode 中常用的是 exactly (表示已經測量完畢,specsize 就是最終大小了),和 at_most (表示最終大小不能超過 specsize)。

public static int getdefaultsize(int size, int measurespec) 

return result;

}

如果 specmode 是 at_most 或 exactly,getdefaultsize 只是簡單的返回了 specsize。

protected final void setmeasureddimension(int measuredwidth, int measuredheight) 

setmeasureddimensionraw(measuredwidth, measuredheight);

}

private void setmeasureddimensionraw(int measuredwidth, int measuredheight)
setmeasureddimension 函式將返回的 specsize 儲存在成員變數中 measurewidth 和 measureheight 中。

這兩個值十分關鍵,就是測量這一步驟的最終結果。

簡化後下**:

protected void onmeasure(int widthmeasurespec, int heightmeasurespec)

view 的大小測量並不是在 view 的 onmeasure 中進行的,就是說上述都不是重點。

<?xml version="1.0" encoding="utf-8"?>

你會看到綠色一片,wrap_content 好像沒有生效。

protected void onmeasure(int widthmeasurespec, int heightmeasurespec) }}

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

setmeasureddimension(resolvesizeandstate(maxwidth, widthmeasurespec, childstate),

resolvesizeandstate(maxheight, heightmeasurespec,

childstate << measured_height_state_shift));

count = mmatchparentchildren.size();

if (count > 1) else

final int childheightmeasurespec;

if (lp.height == layoutparams.match_parent) else

child.measure(childwidthmeasurespec, childheightmeasurespec);}}

}

真正的測量大小工作,是在 viewgroup 的 onmeasure 中進行的。

而 viewgroup 是抽象類,只能找最簡單的 framelayout 來分析下。

protected void measurechildwithmargins(view child,

int parentwidthmeasurespec, int widthused,

int parentheightmeasurespec, int heightused)

public static int getchildmeasurespec(int spec, int padding, int childdimension)  else if (childdimension == layoutparams.match_parent)  else if (childdimension == layoutparams.wrap_content) 

break;

// parent has imposed a maximum size on us

case measurespec.at_most:

if (childdimension >= 0) else if (childdimension == layoutparams.match_parent) else if (childdimension == layoutparams.wrap_content)

break;

//...

}return measurespec.makemeasurespec(resultsize, resultmode);

}

這兩個函式就幹了一件事,確定子 view 的 measurespec,並

child.measure(childwidthmeasurespec, childheightmeasurespec);
child.measure 會呼叫 child.onmeasure ,這樣就會"遞迴"了。

specsize 是這麼算的,parent.specsize - parent.padding - chid.margin 。

specmode 是這麼算的,如果子 view 的 layoutparams 是固定的值,那麼就是 exactly。

如果兩者都是 match_parent 則也是 exactly。

若有一者是 wrap_content,那麼就是 at_most。

在自定義 view 或 viewgroup 時,必須要做好 at_most 的處理,否則 wrap_content 就像無效似得。

就像 2.1 中的例子,預設 view 的 onmeasure 函式只是儲存了 specsize,沒處理 at_most。

看看 framelayout 是怎麼做的,這裡只討論高。

如果 framelayout 的高已經測量完畢了(exactly),那麼完事大吉。

如果 framelayout 的高還沒測量 (at_most),那麼遍歷子 view,記錄最大高度(child.specsize + child.margin + parent.padding)。

然後取 specsize 和 最大值中較小的那乙個,測量完畢。

最後,遍歷 match_parent 的子 view 重新測量一遍,因為高度變了。

view測量學習筆記

view的onmeasure 方法 protected void onmeasure int widthmeasurespec,int heightmeasurespec 獲取控制項最小寬高 protected intgetsuggestedminimumwidth 上面的getdefaultsiz...

Android中View的測量

即精確值模式,當我們將空間的width或height制定為具體值 或者為match parent時,此時將佔據父容器的大小,使用的就是exactly。最大值模式,當空間的寬高屬性制動為自適應wrap content時,控制項大小一般隨著空間的子空間或內容的變化而發生改變,此時view的尺寸只要不超過...

View的測量與繪製

通過measurespec這乙個類,就可以獲取view的測量模式和view想要繪製的大小。measurespec類,是乙個32位的int值,前兩位為測量的模式,測量的模式有三種 exactly,at most,unspecified view類預設的測量view方式為onmeasure 且只支援ex...