Android view绘制流程详解分为两部分:
ViewTree的生成
View的绘制
View的绘制流程
一、ActivityThread的RESUME_ACTIVITY消息
当Activity的H接收到RESUME_ACTIVITY消息的时候,调用了handleResumeActivity方法。
case RESUME_ACTIVITY:
SomeArgs args = (SomeArgs) msg.obj;
handleResumeActivity((IBinder) args.arg1, true, args.argi1 != 0, true,
args.argi3, "RESUME_ACTIVITY");
break;
在handleResumeActivity中:
1、获取Activity
2、通过activity获取phoneWindow
3、通过phoneWindow获取DecorView
4、通过WindowManagerImpl.addView(),将DevorView传入
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
r = performResumeActivity(token, clearHide, reason);
final Activity a = r.activity;
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow(); getWindow获取PhoneWindow对象
View decor = r.window.getDecorView();通过PhoneWindow获取DecorView对象。
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();获取windowManager实现类WindowManagerImpl
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
TYPE_BASE_APPLICATION这个类型表示这个window是所有程序的基础window,所有其他程序都显示在其上面
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient && !a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
} else if (!willBeVisible) {
r.hideForNow = true;
}
。。。。。。。。
}
WindowManagerImpl.java中调用了WindowManagerGlobal类中的addView方法。
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
WindowManagerGlobal.java中首先创建一个RootViewImpl对象,然后调用它的setView()方法。
public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow{
ViewRootImpl root;
synchronized (mLock) {
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
root.setView(view, wparams, panelParentView);
}
perforTraversals()绘制入口
view的绘制流程就是perforTraversals()方法开始的,绘制流程主要分为三个步骤:
- 测量:performMeasure()
- 布局:performLayout()
- 绘制:performDraw()
步骤一、测量:
- MeasureSpec类:它是View的一个静态内部类,MeasureSpec用来封装从父容器传递过来的对子容器的布局要求,它是(子view自身的LayoutParams参数,就是我们在xml布局文件中写的layout_width、layout_height的值转化过来的)和(父容器对子view的约束要求的measureSpec)这两者经过计算得出来的对子view的约束measureSpec。通过一个32位的值来表示测量结果,高2位是mode,低30位是大小。mode分为3种:UNSPECIFIED(0)、EXACTLY(1):具体大小、AT_MOST(2):最大值如wrap_content。
- Measure关键方法一、measureChildWithMargin(),其中有个getChildMeasureSpec()方法用来测量子view的尺寸,共9中情况需要知道。
- Measure关键方法二、setMeasuredDimension()方法,每个view重写onMeasure方法后都需要通过这个方法来保存测量尺寸,保存完了之后,getMeasureWidth()才有效。
- 当子view遍历测量结束之后,将每个view的宽高分别进行累加
private void performTraversals() {
这个mView在setView中传入,其实就是DecorView
final View host = mView;
mIsInTraversal = true;//是否正在遍历
mWillDrawSoon = true;//是否马上需要绘制View
boolean windowSizeMayChange = false;
boolean newSurface = false;
boolean surfaceChanged = false;
这个mWindowAttributes在serView种赋值,其实就是phoneWindow的属性
WindowManager.LayoutParams lp = mWindowAttributes;
顶层视图DecorView所需要的窗口的宽度和高度
int desiredWindowWidth;
int desiredWindowHeight;
.................
Rect frame = mWinFrame;
if (mFirst) { //在构造方法中mFirst已经设置为true,表示是否是第一次绘制DecorView
mFullRedrawNeeded = true;
mLayoutRequested = true;
//这个判断决定了DecorView的高度是否包括状态栏
if (shouldUseDisplaySize(lp)) {
Point size = new Point();
mDisplay.getRealSize(size);
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
} else {
Configuration config = mContext.getResources().getConfiguration();
desiredWindowWidth = dipToPx(config.screenWidthDp);
desiredWindowHeight = dipToPx(config.screenHeightDp);
}
mAttachInfo.mUse32BitDrawingCache = true;
mAttachInfo.mHasWindowFocus = false;
mAttachInfo.mWindowVisibility = viewVisibility;
mAttachInfo.mRecomputeGlobalAttributes = false;
mLastConfiguration.setTo(host.getResources().getConfiguration());
mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
if (mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {
host.setLayoutDirection(mLastConfiguration.getLayoutDirection());
}
host.dispatchAttachedToWindow(mAttachInfo, 0);
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
dispatchApplyInsets(host);
} else {
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
mFullRedrawNeeded = true;
mLayoutRequested = true;
windowSizeMayChange = true;
}
}
..............
if (mFirst || windowShouldResize || insetsChanged ||
viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
mForceNextWindowRelayout = false;
mAttachInfo.mWindowLeft = frame.left;
mAttachInfo.mWindowTop = frame.top;
if (mWidth != frame.width() || mHeight != frame.height()) {
mWidth = frame.width();
mHeight = frame.height();
}
if (!mStopped || mReportNextDraw) {
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
(relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
|| mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
updatedConfiguration) {
主要就是以下三句话:
1、基于PhoneWindow的lp属性,在PhoneWindow中为rootView找到MeasureSpec。mWidth和mHeight表示PhoneWindow可用的宽高,lp.widthhe和lp.height表示PhoneWindow的宽高的lp值。最终返回值是rootView的MeasureSpec值。rootView也就是DecorView。
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
2、核心语句在这里:执行测量的操作
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
int width = host.getMeasuredWidth();
int height = host.getMeasuredHeight();
boolean measureAgain = false;
if (lp.horizontalWeight > 0.0f) {
width += (int) ((mWidth - width) * lp.horizontalWeight);
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
MeasureSpec.EXACTLY);
measureAgain = true;
}
if (lp.verticalWeight > 0.0f) {
height += (int) ((mHeight - height) * lp.verticalWeight);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
MeasureSpec.EXACTLY);
measureAgain = true;
}
//如果当设置了权重不为0的时候,需要在执行一次测量,所以网上很多说relativeLayout测量两次,LinearLayout测量一次,其实是有条件的
if (measureAgain) {
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
layoutRequested = true;
}
}
} else {
maybeHandleWindowMove(frame);
}
执行测量操作,mView是DecorView extends FramLayout extends ViewGroup extends View。
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
View.java 这里measure是用final修饰的,因此,不能被重写,所以view.measure()或者child.measure()方法进入都从这里开始分发。传入的参数用来限制宽高的大小,真正测量在onMeasure中实现。所以这里直接跳到自定义的View中看onMeasure即可,这里的第一个实现view就是DevorView。
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
.........
// measure ourselves, this should set the measured dimension flag back
onMeasure(widthMeasureSpec, heightMeasureSpec);
.........
}
当子view在重写onMeasure的时候,必须重写serMeasureDimension()方法,这个方法不然会报IllegalStateException异常。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
可以看到这个setMeasureDimension方法就是用来保存mMeasureWidth和mMeasureHeight的值的。比如Linear Layout、relativeLayout等都在onMeasure的最后重写了setMeasureDimension方法,只有重写这个方法以后,getMeasureWidth获取值才有效。
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
...............
setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
DevorView.java中的onMeasure方法:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
............这里一堆先计算出自身的MeasureSpec对象,super指的是FramLayout,在super中遍历计算出每个子view的空间大小..........
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
子view遍及计算结束后,回到这里继续执行代码
............
}
FramLayout.java中的中的onMeasure方法:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
这是measure的关键方法。measureChildWithMargins方法用来测量子view所需要的MeasureSpec对象,需要在ViewGroup中统一实现,因为每个容器都会用到。
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
childState = combineMeasuredStates(childState, child.getMeasuredState());
if (measureMatchParentChildren) {
if (lp.width == LayoutParams.MATCH_PARENT ||
lp.height == LayoutParams.MATCH_PARENT) {
mMatchParentChildren.add(child);
}
}
}
}
..............
遍历子view测量结束后,又回到这里,通过serMeasureDimension方法设置。
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
}
ViewGroup中的measureChildWithMargins实现方法,通过getChildMeasureSpec方法计算出子view的约束性空间MeasureSpec,然后继续通过child.measure()方法传递给子view。
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
这个方法是传递measureSpec的关键,分9中情况(wrap_content的计算方法):
如果父view是EXCACTLY:
- 子view值确定,size = childDimension ;mode = EXCACTLY;
- 子view == MATCH_PARENT,size = size ;mode = EXACTLY;
- 子view == WRAP_CONTENT,size = size ;mode = AT_MOST;
如果父view是AT_MOST:
- 子view值确定,size = childDimension ;mode = EXCACTLY;
- 子view == MATCH_PARENT,size = size ;mode = AT_MOST;举例,linearLayout中上面固定高度,下面match_parent的场景,只能说最大时父布局大小。
- 子view == WRAP_CONTENT,size = size ;mode = AT_MOST;
如果父view是UNSPECIFIED:
- 子view值确定,size = childDimension ;mode = UNSPECIFIED;
- 子view == MATCH_PARENT,api23之前时0,之后是size = size ;mode = UNSPECIFIED;
- 子view == WRAP_CONTENT,api23之前时0,之后是size = size ;mode = UNSPECIFIED;
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 = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
Measure大致流程总结:
测量的关键方法是measureChildWithMargin(),里面通过调用getChildMeasureSpec方法计算出子view所需要的空间大小。
步骤二、布局
- layout和measure的区别在与layout的入口是在ViewGroup中,因为只有容器才需要将自己的child执行layout。
- 关键方法一、setFrame(),这个方法中设置了当前View的左上右下的值,并且返回是否改变位置。
- 关键方法二、layoutChildren(),跟measure一样,遍历子view放在FramLayout中实现,通过layoutChildren()方法根据本view的左上右下的值,以及子view的layoutParams属性(gravity和margin的值)计算出子view的左上右下的值。
注意: getWidth()和getHeight()是在setFram()方法之后调用才有效。所以在onCreate中直接获取view的宽高是获取不到的,需要用到ViewTreeObserver方法。
当performMeasure方法执行顺利结束后,会将layoutRequested 赋值为ture结束后,然后继续执行布局。Layout是根据performMeasure方法测量的值和view的属性来确定view摆放的位置。
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
这里很干脆利索,条件成立进来第一件事就是performLayout;
performLayout(lp, mWidth, mHeight);
.............
}
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
mLayoutRequested = false;
mScrollMayChange = true;
mInLayout = true;
final View host = mView;
这里跟measure一样,直接调用了DecorvVew的layout,layout跟measure的区别是只有容器才需要layout,因此这里入口layout也就是final修饰的layout在viewGroup中,因此调用的是ViewGroup.layout();
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
............
}
ViewGroup.java中的layout方法,这里跟measure一样,也是个final修饰,继承者不能重写。因此只要调用view.layout(),或者child.layout()都从这里进入。
public final void layout(int l, int t, int r, int b) {
if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
if (mTransition != null) {
mTransition.layoutChange(this);
}
这里直接调用了super的layout方法;
super.layout(l, t, r, b);
} else {
mLayoutCalledWhileSuppressed = true;
}
}
View.java中的layout方法,这个是layout的关键部分,layout布局就是通过这里的setFrame方法设置左上右下四个点的。
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;
这里通过setFrame方法给当前View的左上右下四个值赋值,并判断View是否发生变化,这个方法放在第一步,基本上通过这个方法就能确定子view摆放的大概位置了。
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
这里跟measure一样,如果当前View发生变化了或者调用了requestLayout,那么就调用onLayout()方法;
onLayout(changed, l, t, r, b);
if (shouldDrawRoundScrollbar()) {
if(mRoundScrollbarRenderer == null) {
mRoundScrollbarRenderer = new RoundScrollbarRenderer(this);
}
} else {
mRoundScrollbarRenderer = null;
}
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;
}
View中的onLayout方法是一个空方法,onLayout是父容器布局子容器用的,所以在view中实现onLayout没有意义,只有ViewGroup中才有意义
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
DecorView.java,onLayout的第一个实现类当然是在DecorView中了,这里进来就调用了super方法。
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
getOutsets(mOutsets);
if (mOutsets.left > 0) {
offsetLeftAndRight(-mOutsets.left);
}
if (mOutsets.top > 0) {
offsetTopAndBottom(-mOutsets.top);
}
if (mApplyFloatingVerticalInsets) {
offsetTopAndBottom(mFloatingInsets.top);
}
if (mApplyFloatingHorizontalInsets) {
offsetLeftAndRight(mFloatingInsets.left);
}
updateElevation();
mAllowUpdateElevation = true;
if (changed && mResizeMode == RESIZE_MODE_DOCKED_DIVIDER) {
getViewRootImpl().requestInvalidateRootRenderNode();
}
}
FramLayout.java,这里跟measure一样,也是在FramLayout中遍历子view来确定各个子view的位置。
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
这里根据父view的坐标位置,以及子view的属性值,如measureWidth、measureHeight、marging、gravity等计算出子view的位置。然后通过child.layout方法层层传递。
void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
final int count = getChildCount();
final int parentLeft = getPaddingLeftWithForeground();
final int parentRight = right - left - getPaddingRightWithForeground();
final int parentTop = getPaddingTopWithForeground();
final int parentBottom = bottom - top - getPaddingBottomWithForeground();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int childLeft;
int childTop;
int gravity = lp.gravity;
if (gravity == -1) {
gravity = DEFAULT_CHILD_GRAVITY;
}
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
//根据位置属性计算出view应该摆放的位置坐标。
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
if (!forceLeftGravity) {
childLeft = parentRight - width - lp.rightMargin;
break;
}
case Gravity.LEFT:
default:
childLeft = parentLeft + lp.leftMargin;
}
switch (verticalGravity) {
case Gravity.TOP:
childTop = parentTop + lp.topMargin;
break;
case Gravity.CENTER_VERTICAL:
childTop = parentTop + (parentBottom - parentTop - height) / 2 +
lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = parentBottom - height - lp.bottomMargin;
break;
default:
childTop = parentTop + lp.topMargin;
}
//计算完当前的位置坐标后,然后继续递归遍历子view
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
Layout大致流程:首先到达ViewGroup中的final layout方法,这个方法是final,然后再super到view中的layout方法,这个中通过setFrame方法计算出左上右下的值,然后开始调用onLayout方法,这在DecorView中也就是自定义布局的view中通过重写onLayout方法重新给子view进行布局摆放
步骤三、绘制
performDraw——draw(得到一个surface)判断是否开启硬件加速,drawSoftware(通过surface.lockcanvas()获取canvas)——canvas.setDensity(mDensity)设置画布的尺寸——view.draw()判断是否透明、绘制滚动条等一些操作,等然后通过onDraw和dispatchDraw去分发消息。注意:过度绘制现象如何避免,1、打开设置的“GPU呈现模式分析”:渲染界面所用的耗时;“调试GPU过度绘制”:分析界面层级:蓝绿粉红;2、通过clipRect方法去进行裁剪。
1、了解surface、window、view之间的关系:
简单来讲,Widown是用来展示surface的,对于非view来说,比如surfaceView和textureView都可以直接获取surface然后通过window加载,但是view需要通过viewRootImpl转为surface。
boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
if (!cancelDraw && !newSurface) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
这里进行绘制操作
performDraw();
} else {
if (isViewVisible) {
// Try again
scheduleTraversals();
} else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).endChangingAnimations();
}
mPendingTransitions.clear();
}
}
mIsInTraversal = false;
}
问题:wrap_content是如何计算。就是那9种情况
引申:动态换肤
动态换肤主要分为两个步骤:
步骤一:重写LayoutInflater的Factory2(为了兼容AppcompotActivity用LayoutInflaterFactory)来重写onCreateView()方法,这个内部类是API11才添加进来的,我们可以在这个方法中重新解析view以及对应的属性,并将他们添加到内存集合中去备用。
步骤二:加载并解析apk资源包,然后获取其中的资源文件,然后通过缓存的view和属性直接进行设置。