ViewRoot & DecorView
- ViewRoot --> ViewRootImpl 連接 WindowManager 和 DecorView漱挚,通過(guò) ViewRoot 完成 View 的三大流程汽煮。
- View 的繪制流程從 ViewRoot 的 performTraversals 方法開(kāi)始
performTraversals的工作流程.png
- DecorView --> 頂級(jí) View拱燃,一般包含一個(gè)豎直方向的 LinearLayout,其中有標(biāo)題欄和內(nèi)容欄。
Android UI 界面架構(gòu)圖.png
MesaureSpec
- MeasureSpec = SpecMode(測(cè)量模式) + SpecSize(規(guī)格大小);
public static class MeasureSpec {
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT;
/**
* 要多大給多大宋梧,用于系統(tǒng)內(nèi)部
*/
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
/**
* 精確大小 包含 match_parent 和 具體數(shù)值
*/
public static final int EXACTLY = 1 << MODE_SHIFT;
/**
* 對(duì)應(yīng) wrap_content, 不能大于父容器指定的大小
*/
public static final int AT_MOST = 2 << MODE_SHIFT;
public static int makeMeasureSpec(int size, int mode) {
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
public static int getMode(int measureSpec) {
return (measureSpec & MODE_MASK);
}
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
}
- MeasureSpec 和 LayoutParams 的對(duì)應(yīng)關(guān)系
View 的工作流程
/**
* <p>
* This is called to find out how big a view should be. The parent
* supplies constraint information in the width and height parameters.
* </p>
*
* <p>
* The actual measurement work of a view is performed in
* {@link #onMeasure(int, int)}, called by this method. Therefore, only
* {@link #onMeasure(int, int)} can and must be overridden by subclasses.
* </p>
*
*
* @param widthMeasureSpec Horizontal space requirements as imposed by the
* parent
* @param heightMeasureSpec Vertical space requirements as imposed by the
* parent
*
* @see #onMeasure(int, int)
*/
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
'''
onMeasure(widthMeasureSpec, heightMeasureSpec);
'''
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/**
*This method must be called by {@link #onMeasure(int, int)} to store the measured width and measured height.
*/
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
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;
}
- ViewGroup 的 measure 過(guò)程
/**
* Ask all of the children of this view to measure themselves, taking into
* account both the MeasureSpec requirements for this view and its padding.
* We skip children that are in the GONE state The heavy lifting is done in
* getChildMeasureSpec.
*
* @param widthMeasureSpec The width requirements for this view
* @param heightMeasureSpec The height requirements for this view
*/
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];
if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
}
}
/**
* Ask one of the children of this view to measure itself, taking into
* account both the MeasureSpec requirements for this view and its padding.
* The heavy lifting is done in getChildMeasureSpec.
*
* @param child The child to measure
* @param parentWidthMeasureSpec The width requirements for this view
* @param parentHeightMeasureSpec The height requirements for this view
*/
protected void measureChild(View child, int parentWidthMeasureSpec,
int parentHeightMeasureSpec) {
final LayoutParams lp = child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
- 在 Activity 啟動(dòng)的時(shí)候獲取某個(gè) View 的寬/高
View 的 measure 過(guò)程和 Activity 的生命周期方法不是同步執(zhí)行的,無(wú)法保證 Activity 執(zhí)行了 onCreate狰挡、onResume 時(shí)某個(gè) View 已經(jīng)測(cè)量完畢捂龄,如果沒(méi)有測(cè)量完,獲得的寬/高就是0.
1) onWindowFocusChanged --> 得到和失去焦點(diǎn)時(shí)調(diào)用(例如加叁,Activity 繼續(xù)執(zhí)行和暫停執(zhí)行)
public void onWindowFocusChanged(boolean hasFocus){
super.onWindowFocusChanged(hasFocus);
if(hasFocus){
int width = view.getMeasuredWidth();
int height = view.getMeasureHeight();
}
}
- view.post(runnable)
通過(guò) post 可以將一個(gè) runnable 投遞到消息隊(duì)列的尾部跺讯,然后等待 Looper調(diào)用此 runnable 的時(shí)候,View也已經(jīng)初始化好了
protected void onStart(){
super.onStart();
view.post(new Runnable(){
@Override
public void run(){
int width = view.getMeasureWidth();
int height = view.getMeasureHeight();
}
});
}
- ViewTreeObserver
使用 ViewTreeObserver 的onGlobalLayoutListener接口殉农,當(dāng) View 樹(shù)的狀態(tài)發(fā)生改變或者 View 樹(shù)內(nèi)部的 View 的可見(jiàn)性發(fā)生改變時(shí),該接口被調(diào)用局荚。
protected void onStart(){
super.onStart();
ViewTreeOnserver observer = view.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener(){
@Override
public void onGlobalLayout(){
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
int width = view.getMeasureWidth();
int height = view.getMeasureHeight();
}
})
}
/**
* Assign a size and position to a view and all of its
* descendants
*
public void layout(int l, int t, int r, int b) {
if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnLayoutChangeListeners != null) {
ArrayList<OnLayoutChangeListener> listenersCopy =
(ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
int numListeners = listenersCopy.size();
for (int i = 0; i < numListeners; ++i) {
listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
}
}
}
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
}
/**
* Called from layout when this view should
* assign a size and position to each of its children.
*/
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
- draw 過(guò)程
1)繪制背景 background.draw
- 繪制自己 onDraw
3)繪制 children dispatchDraw
4)繪制裝飾 onDrawScrollBars
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/
// Step 1, draw the background, if needed
int saveCount;
if (!dirtyOpaque) {
drawBackground(canvas);
}
// skip step 2 & 5 if possible (common case)
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
// we're done...
return;
}
'''
}
自定義 View
- CircleView ---繼承 View 重寫(xiě) onDraw 方法
Demo 地址