我们从view的requestLayout方法开始分析。
public void requestLayout() {
if (mMeasureCache != null) mMeasureCache.clear();
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
// Only trigger request-during-layout logic if this is the view requesting it,
// not the views in its parent hierarchy
ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot != null && viewRoot.isInLayout()) {
if (!viewRoot.requestLayoutDuringLayout(this)) {
return;
}
}
mAttachInfo.mViewRequestingLayout = this;
}
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
if (mParent != null && !mParent.isLayoutRequested()) {
//核心代码 调用父view的requestLayout方法
mParent.requestLayout();
}
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
mAttachInfo.mViewRequestingLayout = null;
}
}
viewParent的实例就是View树的ViewRootImpl对象,因此如果在回溯view树过程中没有中断,将会调用它的requestLayout方法。
# ViewRootImpl
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();//核心代码
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//这里需要关注这个runnable对象
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();//遍历起点
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
//这个方法太长了 只调出重点
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
/** View树遍历的主要三个步骤measure、layout、draw **/
......
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
......
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
......
performDraw();
}
performMeasure、performLayout、performDraw内部依次调用view的绘制流程方法。
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
draw(fullRedrawNeeded);
划重点了,这里的mView就是此ViewRootImpl管理的DecorView。
也就是说调用measure、layout、draw就会触发这个view树的测量、布局和重绘。这部分内容看参看一篇不错的文章
Android视图框架Activity,Window,View,ViewRootImpl理解
何时会触发view树的遍历呢?
- View.requestLayout
- View.setLayoutParams
- View.requestFocus(请求view树的draw过程,但只绘制需要重绘的视图,最终是调用invalidate重绘)
注意这里invalidate()方法虽然最终会调用到performTraversals()方法中,但这时measure和layout流程是不会重新执行的,因为视图没有强制重新测量的标志位,而且大小也没有发生过变化,所以这时只有draw流程可以得到执行。而如果你希望视图的绘制流程可以完完整整地重新走一遍,就不能使用invalidate()方法,而应该调用requestLayout()了。
一般引起invalidate()操作的函数如下:
- 直接调用invalidate()方法,请求重新draw(),但只会绘制调用者本身。
- setSelection():请求重新draw(),但只会绘制调用者本身。
- setVisibility() :当View可视状态在INVISIBLE转换VISIBLE时,会间接调用invalidate()方法,继而绘制该View。
当View的可视状态在INVISIBLE/ VISIBLE 转换为GONE状态时,会间接调用requestLayout() 和invalidate方法。
同时,由于整个个View树大小发生了变化,会请求measure()过程以及draw()过程,同样地,只绘制需要“重新绘制”的视图。 - setEnabled()方法: 请求重新draw(),但不会重新绘制任何视图包括该调用者本身。
- dispatchAppVisibility:当应用程序的可见性发生改变时(如启动一个新的Activity),会调用这个函数;一旦ViewRootImpl受到Visibility改变的消息,就会组织一次遍历。
网友评论