永遠等待 那一日 咱可以出頭天 人生不怕風浪 只怕自己沒志氣 - 五月天
雖然知道講觀念很無聊,但只是筆記的話。。 還是紀錄一下性質吧 :D
自定義View 要override的話有主要幾個function,先講前幾個,下的話會介紹完~~~
之後會再有一篇實作篇 :D
自定義View總共有三個方法比較常用需要override,那就一一介紹吧!
1.作用 : 用來測量View的寬跟高,將View放入父容器的時候需要知道view的寬跟高
2.有可能踩到的雷:
1.需要多次測量與測量不精確,可以在onLayout在精準定位一次
2.Wrap_Content無作用
這些都會在之後的文章提出解決方式。
那就開始吧!
要測量一個View的寬跟高需要兩個東西,一個是 MeasureSpec 一個是 LayoutParams
Q:作用?
A:可以想成是一個測量的工具與方法,是由父類提供的,每個MeasureSpec代表一組寬/高的規格
Q:那有幾種模式呢?
A:主要有
LayoutParams是由View本身提供的,也是ViewGroup的子類,View基本上都是一層一層上去組成Group的
Q:使用的點 ? 聽起來還是好抽象喔 ?
A:常見的有 fill_parent match_parent wrap_content XXdp
可以看下面原始碼也有說到這部分
Q:還是好複雜 ?
A:那就改成舉例子:
當父視圖的測量模式為 EXACTLY,子視圖是xxDp or MATCH_PARENT他的測量模式都為EXACTLY
因為父視圖有指定一個精確大小
若子視圖WRAP_CONTENT 他的測量模式為 AT_MOST,因為子視圖此時自己決定自己大小了。
當父視圖的測量模式為 AT_MOST,子視圖是xxDp,儘管父視圖指定的是最大但子視圖還是自己精確指定了
子視圖MATCH_PARENT,因為子視圖也是給範圍,父視圖給的也是最大大小,可能需要再往上推一層才能知道大小,所以子視圖測量模式是AT_MOST
若子視圖WRAP_CONTENT 他的測量模式為 AT_MOST,因為子視圖此時自己決定自己大小了
下面是原始碼的一部份可以參考。。
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;
}
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) {
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
resultSize = 0;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
resultSize = 0;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
原始碼流程 : measure() -> onMeasure ()
-> getDefaultSize() 根據測量規格計算寬高
-> setMeasuredDimension() 根據存起來
Q:ViewGroup ?
一層一層合併計算
measure() -> onMeasure ()
-> 因為有很多子視圖所以 ... measureChildren() ..measureChild() 計算單一寬高
-> setMeasuredDimension() 根據組合存起來
參考