ViewRoot & DecorView
- ViewRoot --> ViewRootImpl 连接 WindowManager 和 DecorView,通过 ViewRoot 完成 View 的三大流程。
- View 的绘制流程从 ViewRoot 的 performTraversals 方法开始
- DecorView --> 顶级 View,一般包含一个竖直方向的 LinearLayout,其中有标题栏和内容栏。
MesaureSpec
- MeasureSpec = SpecMode(测量模式) + SpecSize(规格大小);
public static class MeasureSpec {
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT;
/**
* 要多大给多大,用于系统内部
*/
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
/**
* 精确大小 包含 match_parent 和 具体数值
*/
public static final int EXACTLY = 1 << MODE_SHIFT;
/**
* 对应 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 的对应关系
View 的工作流程
- measure 过程
/**
*
* This is called to find out how big a view should be. The parent
* supplies constraint information in the width and height parameters.
*
*
*
* 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.
*
*
*
* @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 过程
/**
* 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 启动的时候获取某个 View 的宽/高
View 的 measure 过程和 Activity 的生命周期方法不是同步执行的,无法保证 Activity 执行了 onCreate、onResume 时某个 View 已经测量完毕,如果没有测量完,获得的宽/高就是0.
1) onWindowFocusChanged --> 得到和失去焦点时调用(例如,Activity 继续执行和暂停执行)
public void onWindowFocusChanged(boolean hasFocus){
super.onWindowFocusChanged(hasFocus);
if(hasFocus){
int width = view.getMeasuredWidth();
int height = view.getMeasureHeight();
}
}
- view.post(runnable)
通过 post 可以将一个 runnable 投递到消息队列的尾部,然后等待 Looper调用此 runnable 的时候,View也已经初始化好了
protected void onStart(){
super.onStart();
view.post(new Runnable(){
@Override
public void run(){
int width = view.getMeasureWidth();
int height = view.getMeasureHeight();
}
});
}
- ViewTreeObserver
使用 ViewTreeObserver 的onGlobalLayoutListener接口,当 View 树的状态发生改变或者 View 树内部的 View 的可见性发生改变时,该接口被调用。
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();
}
})
}
- layout 过程
/**
* 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 listenersCopy =
(ArrayList)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 过程
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 重写 onDraw 方法
Demo 地址