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...