一、View的測量過程
1、MeasureSpace :封裝了父View傳遞到子View的佈局要求。是由size(大小) 和modle(模式組成),有三種模式:
- UNSPECIFIED:
父檢視不對子檢視施加任何限制,子檢視可以得到任意想要的大小;
- EXACTLY:
父檢視希望子檢視的大小是specSize中指定的大小;
- AT_MOST:
子檢視的大小最多是specSize中的大小。
2、measure(int widthMeasureSpec, int heightMeasureSpec):當父View對子view進行測量時,會呼叫的方法。兩個入參分別代表對子View的限制。
-
1)先判斷是否需要進行測量: View.measure()方法時View並不是立即就去測量,而是先判斷一下是否有必要進行測量操作,如果不是強制測量或者MeasureSpec與上次的MeasureSpec相同的時候,那麼View就不需要重新測量了.
-
2)從快取中讀取是否測量過: 如果不滿足上面條件,View就考慮去做測量工作了.但在測量之前,View還想偷懶,如果能在快取中找到上次的測量結果,那直接從快取中獲取就可以了。它會以MeasureSpec計算出的key值作為鍵,去成員變數mMeasureCache中查詢是否快取過對應key的測量結果,
-
3)onMeasure()方法進行測量: 如果不能從mMeasureCache中讀到快取過的測量結果,只能乖乖地呼叫onMeasure()方法去完成實際的測量工作,並且將尺寸限制條件widthMeasureSpec和heightMeasureSpec傳遞給onMeasure()方法。
-
4)儲存測量結果: 最終View都會得到測量的結果,並且將結果儲存到mMeasuredWidth和mMeasuredHeight這兩個成員變數中,同時快取到成員變數mMeasureCache中,以便下次執行measure()方法時能夠從其中讀取快取值
3、onMeasure()
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
複製程式碼
4、setMeasuredDimension()
- 1)通過setMeasuredDimensionRaw() ,把測量完的寬高值賦值給mMeasuredWidth、mMeasuredHeight
二、
1、ViewGroup怎麼知道他的子View是多大呢?View提供了以下三組方法:
- getMeasuredWidth()和getMeasuredHeight()
- getMeasuredWidthAndState()和getMeasuredHeightAndState()
- getMeasuredState()
2、mMeasuredWidth是一個Int型別的值,其是由4個位元組組成的。
- 其高位的第一個位元組為第一部分,用於標記測量完的尺寸是不是達到了View想要的寬度,我們稱該資訊為測量的state資訊。
- 其低位的三個位元組為第二部分,用於儲存測量到的寬度。
- 有點類似於measureSpec
3、resolveSizeAndState()
- 1)這個方法的程式碼結構跟前文提到的getDefaultSize()方法很相似。
三、ViewGroup的measure過程
1、對於ViewGroup來說,除了完成自己的measure過程,還會遍歷去呼叫所有子元素的measure()方法,各個子元素再遞迴去執行這個過程
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
final int size = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < size; ++i) {
final View child = children[i];
//遍歷每個子元素,如果該子元素不是GONE的話,就去測量該子元素
if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
}
}
複製程式碼
2、對每個子view 再次進行測量
protected void measureChild(View child, int parentWidthMeasureSpec,
int parentHeightMeasureSpec) {
//獲取child自身的LayoutParams屬性
final LayoutParams lp = child.getLayoutParams();
//根據父佈局的MeasureSpec,父佈局的padding和child的LayoutParams這三個引數,通過getChildMeasureSpec()方法計算出子元素的MeasureSpec
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom, lp.height);
//呼叫measure()方法測量child,前文已經解釋過這個方法,
//呼叫該方法之後會將view的寬高值儲存在mMeasuredWidth和mMeasuredHeight這兩個屬性當中,這樣child的尺寸就已經測量出來了
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
複製程式碼
measureChild()的思想就是取出子元素的LayoutParams,然後再通過getChildMeasureSpec()方法來建立子元素的MeasureSpec,接著將MeasureSpec傳給View的measure()方法來完成對子元素的測量。
3、getChildMeasureSpec(int spec, int padding, int childDimension)
- getChildMeasureSpec()這個方法清楚展示了普通View的MeasureSpec的建立規則,每個View的MeasureSpec狀態量由其直接父View的MeasureSpec和View自身的屬性LayoutParams(LayoutParams有寬高尺寸值等資訊)共同決定。
4、MeasureSpec狀態後,將其與尺寸值通過makeMeasureSpec(int size,int mode)方法結合在一起,就是最終傳給View的onMeasure(int, int)方法的MeasureSpec值了。
5、ViewRootImpl是連線WindowManager和DecorView的紐帶,控制元件的測量、佈局、繪製以及輸入事件的分發處理都由ViewRootImpl觸發。
performTraversals()它呼叫了一個performTraversals()方法使得View樹開始三大工作流程
private void performTraversals() {
...
if (!mStopped || mReportNextDraw) {
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
}
}
if (didLayout) {
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
...
}
if (!cancelDraw && !newSurface) {
performDraw();
}
...
}
複製程式碼
6、performTraversals() 通過呼叫performMeasure()、performLayout()、performDraw()這三個方法,這三個方法分別完成DecorView的measure、layout、和draw這三大流程,其中performMeasure()中會呼叫measure()方法,在measure()方法中又會呼叫onMeasure()方法,在onMeasure()方法中會對所有子元素進行measure過程,這個時候measure流程就從父容器傳遞到子元素中了,這樣就完成了一次measure過程。
7、樹是遍歷順序,這意味著父View將被先繪製,子View檢視後被繪製。
8、在measure過程中,通過 widthMeasureSpec() heightMeasureSpec 以及View自身的LayoutParams共同決定子檢視的測量規格;
-
1)MeasureSpec :父View對子View的測量模式、測量大小的要求。
-
2)子View 檢視通過LayoutParams,這個類並告訴父View 檢視他們應該怎樣被測量和放置。
-
3)它的尺寸可以有三種表示方法:
1、具體數值 2、FILL_PARENT 3、WRAP_CONTENT
9、對於不同的ViewGroup的子類,有著各自不同的LayoutParams。
10、getMeasuredWidth()、getMeasuredHeight()返回的是measure過程得到的mMeasuredWidth和mMeasuredHeight的值,而getWidth()和getHeight()返回的是mRight - mLeft和mBottom - mTop的值。
public final int getMeasuredWidth() {
return mMeasuredWidth & MEASURED_SIZE_MASK;
}
public final int getWidth() {
return mRight - mLeft;
}
複製程式碼
11、draw方法步驟:繪製背景---------繪製自己--------繪製chrildren----繪製裝飾。