[toc]
流式布局
自定義view與自定義viewgroup的區(qū)別
- 自定義view一般繼承View等其他View,通常實現(xiàn)onMeasure與onDraw方法
- 自定義ViewGroup一般是利用現(xiàn)有的組件根據(jù)特定的布局方式來組成新的組件,大多繼承ViewGroup或各種Layout,通常實現(xiàn)onMeasure與onLayout
Android坐標系說明
兩種坐標系

Android屏幕坐標系png.png

視圖坐標系.png
MeasureSpec講解
MeasureSpace是View的內部類,是int類型的,用高兩位表示mode,低30位表示size
屬性說明
- UNSPECIFIED 不對View大小進行限制,通常為系統(tǒng)使用,不用太關注
- EXACTLY 確切的大小,比如100dp
- AT_MOST 大小不可超過某個數(shù)值,比如 matchParent最大不能超過父布局
普通View的Measure的創(chuàng)建規(guī)則
通過表格快查子View的mode
普通View的Measure的創(chuàng)建規(guī)則
通過系統(tǒng)代碼解析子View的MeasureSpec創(chuàng)建過程
一般是通過調用系統(tǒng)的 getChildMeasureSpec 獲取子View的MeasureSpec,代碼解析如下:
/**
* @param spec The requirements for this view(父元素ViewGroup的MeasureSpec)
* @param padding The padding of this view for the current dimension and margins, if applicable (父元素的padding值)
* @param childDimension How big the child wants to be in the current dimension(需要被測量的子View的) 屬性值
* View.getLayoutParams().width/height
*/
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
// 獲取父View的Mode與Size
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
// 計算父View留給子View的具體尺寸(去除padding值的尺寸)
int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;
// 判斷父View的mode
switch (specMode) {
// Parent has imposed an exact size on us
// 如果父View是 EXACTLY (確定值)
// 子View為 具體尺寸 比如 100dp,則子view的mode為 EXACTLY,最大尺寸就是childDimension
// 子View為 MATCH_PARENT, 則子view的mode為 EXACTLY,最大尺寸為 size(父View留給子View的最大尺寸)
// 子View為 WRAP_CONTENT, 則子view的mode為 AT_MOST,最大尺寸為 size(父View留給子View的最大尺寸)
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
// 如果父View是 AT_MOST (大小與內容有關)
// 子View為 具體尺寸 比如 100dp,則子view的mode為 EXACTLY,最大尺寸就是childDimension
// 子View為 MATCH_PARENT, 則子view的mode為 AT_MOST,最大尺寸為 size(父View留給子View的最大尺寸)
// 子View為 WRAP_CONTENT, 則子view的mode為 AT_MOST,最大尺寸為 size(父View留給子View的最大尺寸)
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
// 此為系統(tǒng)調用,暫不分析 具體同上
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
// 計算子View的 MeasureSpec 并 返回
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
getWidth與getMeasureWidth區(qū)別
- getWidth是在onLayout以后才能獲取到,是通過視圖右邊的坐標減去左邊的坐標計算出來的
- getMeasureWidth是在onMeasure結束以后才可以獲取到的值,是通過setMeasuredDimension設置的值
coding
補充說明
onMeasure調用次數(shù)
- onMeasure的調用次數(shù)與父View的算法有關系,比如:FrameLayout調用了子View的onMeasure有2次。