performTraversals()
上文提过,在setView时会调用requestLayout()触发遍历。遍历操作指的就是performTraversals()方法。ViewRootImpl中接受到的各种变化如重绘等请求都引发performTraversals()方法调用,并处理。View类及子类中的onMeasure(),onLayout(),onDraw()方法将会在performTraversals()中被回调。
话不多说,上代码
private void performTraversals() {
``````
//主角,下面都是为这两个变量赋值
intdesiredWindowWidth;
intdesiredWindowHeight;
``````
if(mFirst) {
/*第一次遍历,此时窗口刚刚被添加到WMS */
if(lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL
|| lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
Point size = new Point();
mDisplay.getRealSize(size);
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
// desiredWindowWidth/Height,其取值是屏幕尺寸
}else {
//应用可以使用的最大尺寸作为SPEC_SIZE的候选
DisplayMetrics packageMetrics =
mView.getContext().getResources().getDisplayMetrics();
desiredWindowWidth = packageMetrics.widthPixels;
desiredWindowHeight = packageMetrics.heightPixels;
}
/* 控件树即将第一次被显示在窗口上,
mAttachInfo一些字段被赋值
然后通过mView发起了dispatchAttachedToWindow()的调用
之后每一个位于控件树中的控件都会回调onAttachedToWindow() */
......
} else {
// 在非第一次遍历的情况下,会采用窗口的最新尺寸作为SPEC_SIZE的候选
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
// 如果窗口的最新尺寸与ViewRootImpl中的现有尺寸不同
if(desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
// 需要进行完整的重绘以适应新的窗口尺寸
mFullRedrawNeeded = true;
// 需要对控件树进行重新布局
mLayoutRequested = true;
windowSizeMayChange = true;
}
}
···
/* 执行位于RunQueue中的回调。RunQueue是ViewRootImpl的一个静态成员,进程唯一,调用view.post(Runable) 时,也将交由attachInfo的handler处理。
getRunQueue().executeActions(attachInfo.mHandler);
booleanlayoutRequested = mLayoutRequested && !mStopped;
/* 仅当layoutRequested为true时才进行预测量。
layoutRequested为true表示在进行“遍历”之前requestLayout()方法被调用过。
requestLayout()方法用于要求ViewRootImpl进行一次“遍历”并对控件树重新进行测量与布局 */
if(layoutRequested) {
final Resources res = mView.getContext().getResources();
if(mFirst) {
mAttachInfo.mInTouchMode = !mAddedTouchMode;
ensureTouchModeLocally(mAddedTouchMode);// 确定控件树是否需要进入TouchMode,下一章节将会详细说。
}else {
···
}
// 其他阶段的处理,对主角进行测量
// Ask host how big it wants to be
windowSizeMayChange |= measureHierarchy(host, lp, res,
desiredWindowWidth, desiredWindowHeight);
......
//对主角进行布局
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
·······
//最后调用
performDraw();
}
其中,measureHierarchy()中主要调用了
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
完成测量过程.
小结
·performTraversals方法会经过measure、layout和draw三个过程才能将一个View绘制出来,所以View的绘制是ViewRootImpl完成的,另外当手动调用invalidate,postInvalidate,requestInvalidate也会最终调用performTraversals,来重新绘制View。
·RunQueue是ViewRootImpl的一个静态成员,当我们在子线程更新ui时会调用view.post()或者view.postDelay()都会将runable交由attachInfo中的handler来处理,从而触发performTraversals()完成ui更新。
·事件分发也是由ViewRootImpl完成。
网友评论