/**
* performTraversals()函数正是系统内进行View树遍历工作的核心函数,该函数内部逻辑稍有复杂,
* 一个函数代码长度约600行。虽然该函数的代码很长,但其主体逻辑却是很清晰的,其执行过程可简单
* 概括为根据之前所有设置好的状态,判断是否需要重新计算视图大小 measure)、是否需要重新安置视
* 图 的 位 置 layout),以及是否需要重绘 draw)视图,
*/
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals");
host.debug();
}
/**一、处理mAttachlnfo的初始化,并根据resize和visibility改变的情况,给相应的变量赋值。
*
*/
if (host == null || !mAdded)
return;
mTraversalScheduled = false;
mWillDrawSoon = true;
boolean windowResizesToFitContent = false;
boolean fullRedrawNeeded = mFullRedrawNeeded;
boolean newSurface = false;
boolean surfaceChanged = false;
WindowManager.LayoutParams lp = mWindowAttributes;
int desiredWindowWidth;
int desiredWindowHeight;
int childWidthMeasureSpec;
int childHeightMeasureSpec;
final View.AttachInfo attachInfo = mAttachInfo;
final int viewVisibility = getHostVisibility();
boolean viewVisibilityChanged = mViewVisibility != viewVisibility
|| mNewSurfaceNeeded;
float appScale = mAttachInfo.mApplicationScale;
WindowManager.LayoutParams params = null;
if (mWindowAttributesChanged) {
mWindowAttributesChanged = false;
surfaceChanged = true;
params = lp;
}
Rect frame = mWinFrame;
/**
* ) 如果是第一次进行View树遍历,则对mAttachlnfo中的变量进行初始化。所谓的 “第一次”
* mFirst变量是指,该窗口创建好以后的第一次显示,在后面整个运行过程中,都不是第一次,直到应用
* 程序退出,该窗口被销毁为止。初始化后,调用host.dispatchAttachedToWindow(),参数包含mAttachlnfo,
* 所有的子视图都将把mAttachlnfo的值复制到自己的mAttachlnfo变量中。
*/
if (mFirst) {
fullRedrawNeeded = true;
mLayoutRequested = true;
DisplayMetrics packageMetrics =
mView.getContext().getResources().getDisplayMetrics();
desiredWindowWidth = packageMetrics.widthPixels;
desiredWindowHeight = packageMetrics.heightPixels;
// For the very first time, tell the view hierarchy that it
// is attached to the window. Note that at this point the surface
// object is not initialized to its backing store, but soon it
// will be (assuming the window is visible).
attachInfo.mSurface = mSurface;
attachInfo.mUse32BitDrawingCache = PixelFormat.formatHasAlpha(lp.format) ||
lp.format == PixelFormat.RGBX_8888;
attachInfo.mHasWindowFocus = false;
attachInfo.mWindowVisibility = viewVisibility;
attachInfo.mRecomputeGlobalAttributes = false;
attachInfo.mKeepScreenOn = false;
viewVisibilityChanged = false;
mLastConfiguration.setTo(host.getResources().getConfiguration());
host.dispatchAttachedToWindow(attachInfo, 0);
//Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
} else {
/**
* ) 如果不是第一次,则查看是否resize过,resize—般是用于输入窗口显示后,WmS通知客户
* 端窗口调整窗口大小,resize发生后,客户窗口需要做以下工作。
* • 将 fUllRedrawNeeded置为true,即需要全部重绘。
* • 将 mLayoutRequested置为true,即需要重新为视图指定位置
* • 将 windowResizesToFitContent置为true,该变量将在下面被使用
*/
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
//判断resize
if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
if (DEBUG_ORIENTATION) Log.v(TAG,
"View " + host + " resized to: " + frame);
fullRedrawNeeded = true;
mLayoutRequested = true;
windowResizesToFitContent = true;
}
}
/**
* 如果窗口的visibility状态发生改变,比如窗口从后台切换到前台或者相反,则给mAttachlnfo
* 中的变量 mWindowVisibility 赋新值,并调用 host.dispatchWindowVisibilityChanged()
* 函数将这个变化信息传递给所有的子视图。
*/
if (viewVisibilityChanged) {
attachInfo.mWindowVisibility = viewVisibility;
host.dispatchWindowVisibilityChanged(viewVisibility);
if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
if (mUseGL) {
destroyGL();
}
}
if (viewVisibility == View.GONE) {
// After making a window gone, we will count it as being
// shown for the first time the next time it gets focus.
mHasHadWindowFocus = false;
}
}
boolean insetsChanged = false;
/**
* 如果需要重新布局,即 mRequestedLayout变量为true,则首先需要重新计算所有视图的大小。
* 换句话说,要重新layout,就必须重新measure。
*/
if (mLayoutRequested) {
// Execute enqueued actions on every layout in case a view that was detached
// enqueued an action after being detached
getRunQueue().executeActions(attachInfo.mHandler);
/**
* )如果 mFirst为true,则调用 host.fitSystemWindow(attachlnfo.mContentlnsets)。
* 参数 mContentlnsets
* 是 WmS为应用程序设置的“嵌入区”,inset本 意 是 “嵌入”、“插图”,Content是指视图区用于真正显
* 示内容的区域,所以,mContentlnsets的大小一般是指状态栏窗口的大小,当应用程序全屏运行时,
* mContentlnsets的大小为0。这里有意思的是该变量的类型是一个Rect,而这个insets的大小是由WmS
* 指定的,问题是WmS如何把这个矩形信息传递给客户窗口呢?答案就是Rect实现了 Pacelable接口,
* 客户端可以创建一个 Rect变量,并把这个变量传递给WmS,然后由WmS修改后再返回。
* fitSystemWindow()函数的作用是告沂窗口中所有子视图根据该Inset调整自己的布局,实际上就是用
* inset大小改变视图的mPaddingXXX的值
*/
if (mFirst) {
host.fitSystemWindows(mAttachInfo.mContentInsets);
// make sure touch mode code executes by setting cached value
// to opposite of the added touch mode.
mAttachInfo.mInTouchMode = !mAddedTouchMode;
ensureTouchModeLocally(mAddedTouchMode);
} else {
/**
* 如果不是第一次,则继续执行以下流程。
* (2)判 断 insets是否有变化,如果变化了,也需要调用host.fitSystemWindow()通知窗口中的视图
* 修改mPaddingXXX参数值。
*/
if (!mAttachInfo.mContentInsets.equals(mPendingContentInsets)) {
mAttachInfo.mContentInsets.set(mPendingContentInsets);
host.fitSystemWindows(mAttachInfo.mContentInsets);
insetsChanged = true;
if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: "
+ mAttachInfo.mContentInsets);
}
/**
* 如果mAttachInfo的mVisibleInsets有改变,则将mPendingVisibleInsets
* 赋值给mVisibleInsets,该矩形变量一般为空。
*/
if (!mAttachInfo.mVisibleInsets.equals(mPendingVisibleInsets)) {
mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "
+ mAttachInfo.mVisibleInsets);
}
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
|| lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
windowResizesToFitContent = true;
DisplayMetrics packageMetrics =
mView.getContext().getResources().getDisplayMetrics();
desiredWindowWidth = packageMetrics.widthPixels;
desiredWindowHeight = packageMetrics.heightPixels;
}
}
/**
* 根据窗口宽度获得子视图大小的“测量标准”,其中宽度标准和高度标准分别保存到变量
* childWidthMeasureSpec 和 childHeightMeasureSpec,以下简称 widthSpec 和 heightSpec。而这两个变量的
* 本步操作中,有三个相关的变量容易混淆,一 个 是 lp,一 个 是 desiredWindowWidth,另一个是
* measureSpec。
*lp 变量代表的是根视图的LayoutParams、lp.width或 者 lp.height直接来源于用户的定义,比如
* WRAP—CONTENT、MATCH_PARENT 等。
* desiredWindowWidth是 指 实 际 窗 口 在 屏 幕 上 的 大 小 , 默 认 情 况 下 就 是 屏 幕 大 小 , 即
* displayMetrics.widthPixels, 当窗口大小改变后,其值将变为mWinFrame的大小。比如当有输入法窗口
* 时,mWinFrame的大小是屏幕大小减去输入法窗口的大小。
* measureSpec 是调用 getRootMeasureSpec()函数的返回值,分两种,分别是 MeasureSpec.EXACTLY
* 和 MeasureSpec.AT_MOST, 这两个常量分别是 0x4000 0000 和 0x8000 0000。measureSpec 是调用
* MeasureSpec.makeMeasureSpec (size,类型 产生真正的 measureSpec 值,size 代表了真正的大小,换
* 句话说,该变量的高16位代表类型,低 16位代表实际大小。measureSpec将作为测量子视图大小的标
* 准,
*/
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
// Ask host how big it wants to be
if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(TAG,
"Measuring " + host + " in display " + desiredWindowWidth
+ "x" + desiredWindowHeight + "...");
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals -- after measure");
host.debug();
}
}
/**
* 查 看 mAttachlnfo中 的 mRecomputeGlobalAttributes是 否 为 true。该变
* 量的含义是有子视图的
* visibility等属性发生过了改变, ViewRoot需要重新获取这些属性,于是调用
* host.dispatchCollectViewAttributes(O) , 参数 0 代表 View.VISIBILITY
*/
if (attachInfo.mRecomputeGlobalAttributes) {
//Log.i(TAG, "Computing screen on!");
attachInfo.mRecomputeGlobalAttributes = false;
boolean oldVal = attachInfo.mKeepScreenOn;
attachInfo.mKeepScreenOn = false;
host.dispatchCollectViewAttributes(0);
if (attachInfo.mKeepScreenOn != oldVal) {
params = lp;
//Log.i(TAG, "Keep screen on changed: " + attachInfo.mKeepScreenOn);
}
}
/**
* 如果是第一次,或者发生了 visibility变化,则需要修正lp中的 softlnputMode。当然这只是在
* softlnputMode没有指定的情况下, 如果指定了就不用修改。
*/
if (mFirst || attachInfo.mViewVisibilityChanged) {
attachInfo.mViewVisibilityChanged = false;
int resizeMode = mSoftInputMode &
WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
// If we are in auto resize mode, then we need to determine
// what mode to use now.
if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
final int N = attachInfo.mScrollContainers.size();
for (int i=0; i<N; i++) {
if (attachInfo.mScrollContainers.get(i).isShown()) {
resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
}
}
if (resizeMode == 0) {
resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
}
if ((lp.softInputMode &
WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) != resizeMode) {
lp.softInputMode = (lp.softInputMode &
~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) |
resizeMode;
params = lp;
}
}
}
if (params != null && (host.mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) != 0) {
if (!PixelFormat.formatHasAlpha(params.format)) {
params.format = PixelFormat.TRANSLUCENT;
}
}
boolean windowShouldResize = mLayoutRequested && windowResizesToFitContent
&& ((mWidth != host.mMeasuredWidth || mHeight != host.mMeasuredHeight)
|| (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT &&
frame.width() < desiredWindowWidth && frame.width() != mWidth)
|| (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT &&
frame.height() < desiredWindowHeight && frame.height() != mHeight));
final boolean computesInternalInsets =
attachInfo.mTreeObserver.hasComputeInternalInsetsListeners();
boolean insetsPending = false;
int relayoutResult = 0;
/**
* 无论是resize还 是 inset变化了,或者是窗口的visibility发生变化了,都意味着需要重新设置
* 客户窗口的大小,并重新计算窗口中视图的大小。请注意区分本步操作和第2 步操作的区别,第 1 步
* 是指窗口内部视图变化引起的重新计算大小,而本步是指窗口本身大小发生了变化。
*/
if (mFirst || windowShouldResize || insetsChanged
|| viewVisibilityChanged || params != null) {
if (viewVisibility == View.VISIBLE) {
// If this window is giving internal insets to the window
// manager, and it is being added or changing its visibility,
// then we want to first give the window manager "fake"
// insets to cause it to effectively ignore the content of
// the window during layout. This avoids it briefly causing
// other windows to resize/move based on the raw frame of the
// window, waiting until we can finish laying out this window
// and get back to the window manager with the ultimately
// computed insets.
insetsPending = computesInternalInsets
&& (mFirst || viewVisibilityChanged);
if (mWindowAttributes.memoryType == WindowManager.LayoutParams.MEMORY_TYPE_GPU) {
if (params == null) {
params = mWindowAttributes;
}
mGlWanted = true;
}
}
if (mSurfaceHolder != null) {
mSurfaceHolder.mSurfaceLock.lock();
mDrawingAllowed = true;
}
boolean initialized = false;
boolean contentInsetsChanged = false;
boolean visibleInsetsChanged;
boolean hadSurface = mSurface.isValid();
try {
int fl = 0;
if (params != null) {
fl = params.flags;
if (attachInfo.mKeepScreenOn) {
params.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
}
}
if (DEBUG_LAYOUT) {
Log.i(TAG, "host=w:" + host.mMeasuredWidth + ", h:" +
host.mMeasuredHeight + ", params=" + params);
}
/**
* (1 )调用relayoutWindow(),该函数内部则会调用sWindowSession.relayout()请 求 WmS按照指定的
* 大小重新分配窗口大小,
* 最后一个参数mSurfkce是客户窗口创建的,WmS内部将为该Surface对象分配真正的显存, 等该
* 函数返回后,应用程序就可以在该Surface中绘制了
*
* 另外,参 数 mPendingContentlnsets、mPendingVisblelnsets都是输出参数,WmS会给这些变量中填
* 入新值。
*/
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
if (params != null) {
params.flags = fl;
}
if (DEBUG_LAYOUT) Log.v(TAG, "relayout: frame=" + frame.toShortString()
+ " content=" + mPendingContentInsets.toShortString()
+ " visible=" + mPendingVisibleInsets.toShortString()
+ " surface=" + mSurface);
if (mPendingConfiguration.seq != 0) {
if (DEBUG_CONFIGURATION) Log.v(TAG, "Visible with new config: "
+ mPendingConfiguration);
updateConfiguration(mPendingConfiguration, !mFirst);
mPendingConfiguration.seq = 0;
}
contentInsetsChanged = !mPendingContentInsets.equals(
mAttachInfo.mContentInsets);
visibleInsetsChanged = !mPendingVisibleInsets.equals(
mAttachInfo.mVisibleInsets);
/**
* (2)如 果 mPendingContentlnsets发生了改变,则需
* 要调用fitSystemWindow()通知所有子窗口根据
* 这种改变调整自身的大小
*/
if (contentInsetsChanged) {
mAttachInfo.mContentInsets.set(mPendingContentInsets);
host.fitSystemWindows(mAttachInfo.mContentInsets);
if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: "
+ mAttachInfo.mContentInsets);
}
/**
* 如果mPendingVisibleInsets发生了改变,则需要将改变的值赋给mAttachInfo的
* mVisibleInsets
*/
if (visibleInsetsChanged) {
mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "
+ mAttachInfo.mVisibleInsets);
}
/**
* 如果之前Surface无效,而此时有效了,则需要设置两个变量的值,一个是设置newSurface
* 为 true,另一个是设置fullRedrawNeeded为 true。本步中的条件的判断有点意思,
*
* 其中变量hadSurface是在调用relayoutWindow()之前赋值的,其值为mSurface.isValide()的返回值。
* 这也就是为什么这个变量叫做hadSurface而不是hasSurface的原因,因为它保存的是“之前” 是否有。
* 而当调用relayoutWindow()后,再次调用mSurface.isValid()肯定会返回true。所以,本步条件成立的情
* 况实际上就是当mFirst为 true时发生。
*/
if (!hadSurface) {
if (mSurface.isValid()) {
// If we are creating a new surface, then we need to
// completely redraw it. Also, when we get to the
// point of drawing it we will hold off and schedule
// a new traversal instead. This is so we can tell the
// window manager about all of the windows being displayed
// before actually drawing them, so it can display then
// all at once.
newSurface = true;
fullRedrawNeeded = true;
mPreviousTransparentRegion.setEmpty();
if (mGlWanted && !mUseGL) {
initializeGL();
initialized = mGlCanvas != null;
}
}
} else if (!mSurface.isValid()) {
// If the surface has been removed, then reset the scroll
// positions.
mLastScrolledFocus = null;
mScrollY = mCurScrollY = 0;
if (mScroller != null) {
mScroller.abortAnimation();
}
}
} catch (RemoteException e) {
}
if (DEBUG_ORIENTATION) Log.v(
TAG, "Relayout returned: frame=" + frame + ", surface=" + mSurface);
/**
* 给 mAttachlnfo 中变量 mWindowLeft 和 mWindowTop 赋新值。该新值保存在 mWinFrame 中
* 而该变量是在调用sWindowSession.relayout()时作为参数传递给WmS的,WmS会对该值进行修改。
*/
attachInfo.mWindowLeft = frame.left;
attachInfo.mWindowTop = frame.top;
// !!FIXME!! This next section handles the case where we did not get the
// window size we asked for. We should avoid this by getting a maximum size from
// the window session beforehand.
mWidth = frame.width();
mHeight = frame.height();
/**
* 处理 SurfaceHolder.Callback 逻辑
*/
if (mSurfaceHolder != null) {
// The app owns the surface; tell it about what is going on.
if (mSurface.isValid()) {
// XXX .copyFrom() doesn't work!
//mSurfaceHolder.mSurface.copyFrom(mSurface);
mSurfaceHolder.mSurface = mSurface;
}
mSurfaceHolder.mSurfaceLock.unlock();
if (mSurface.isValid()) {
if (!hadSurface) {
mSurfaceHolder.ungetCallbacks();
mIsCreating = true;
mSurfaceHolderCallback.surfaceCreated(mSurfaceHolder);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceCreated(mSurfaceHolder);
}
}
surfaceChanged = true;
}
if (surfaceChanged) {
mSurfaceHolderCallback.surfaceChanged(mSurfaceHolder,
lp.format, mWidth, mHeight);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceChanged(mSurfaceHolder, lp.format,
mWidth, mHeight);
}
}
}
mIsCreating = false;
} else if (hadSurface) {
mSurfaceHolder.ungetCallbacks();
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
mSurfaceHolderCallback.surfaceDestroyed(mSurfaceHolder);
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceDestroyed(mSurfaceHolder);
}
}
mSurfaceHolder.mSurfaceLock.lock();
// Make surface invalid.
//mSurfaceHolder.mSurface.copyFrom(mSurface);
mSurfaceHolder.mSurface = new Surface();
mSurfaceHolder.mSurfaceLock.unlock();
}
}
if (initialized) {
mGlCanvas.setViewport((int) (mWidth * appScale + 0.5f),
(int) (mHeight * appScale + 0.5f));
}
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
(relayoutResult&WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE) != 0);
/**
* 最终,如果窗口尺寸发生了改变,贝U调用hostmeasureO重新计算窗口中视图的大小
*
*/
if (focusChangedDueToTouchMode || mWidth != host.mMeasuredWidth
|| mHeight != host.mMeasuredHeight || contentInsetsChanged) {
childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
if (DEBUG_LAYOUT) Log.v(TAG, "Ooops, something changed! mWidth="
+ mWidth + " measuredWidth=" + host.mMeasuredWidth
+ " mHeight=" + mHeight
+ " measuredHeight" + host.mMeasuredHeight
+ " coveredInsetsChanged=" + contentInsetsChanged);
// Ask host how big it wants to be
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
// Implementation of weights from WindowManager.LayoutParams
// We just grow the dimensions as needed and re-measure if
// needs be
int width = host.mMeasuredWidth;
int height = host.mMeasuredHeight;
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;
}
if (measureAgain) {
if (DEBUG_LAYOUT) Log.v(TAG,
"And hey let's measure once more: width=" + width
+ " height=" + height);
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
mLayoutRequested = true;
}
}
final boolean didLayout = mLayoutRequested;
boolean triggerGlobalLayoutListener = didLayout
|| attachInfo.mRecomputeGlobalAttributes;
/**
* 接下来,根据以上步骤的执行结果,判断是否需要进行重新布局。比如当任意视图的大小发生
* 变化时,它会影响其他视图的布局,如果是,则调用host.layout()进行真正的布局操作
* 。该函数的内部
* 详细流程参见后面小节
*/
if (didLayout) {
mLayoutRequested = false;
mScrollMayChange = true;
if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(
TAG, "Laying out " + host + " to (" +
host.mMeasuredWidth + ", " + host.mMeasuredHeight + ")");
long startTime = 0L;
if (Config.DEBUG && ViewDebug.profileLayout) {
startTime = SystemClock.elapsedRealtime();
}
host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);
if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
if (!host.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_LAYOUT)) {
throw new IllegalStateException("The view hierarchy is an inconsistent state,"
+ "please refer to the logs with the tag "
+ ViewDebug.CONSISTENCY_LOG_TAG + " for more infomation.");
}
}
if (Config.DEBUG && ViewDebug.profileLayout) {
EventLog.writeEvent(60001, SystemClock.elapsedRealtime() - startTime);
}
// By this point all views have been sized and positionned
// We can compute the transparent area
if ((host.mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) != 0) {
// start out transparent
// TODO: AVOID THAT CALL BY CACHING THE RESULT?
host.getLocationInWindow(mTmpLocation);
mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],
mTmpLocation[0] + host.mRight - host.mLeft,
mTmpLocation[1] + host.mBottom - host.mTop);
host.gatherTransparentRegion(mTransparentRegion);
if (mTranslator != null) {
mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
}
if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
mPreviousTransparentRegion.set(mTransparentRegion);
// reconfigure window manager
try {
sWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
} catch (RemoteException e) {
}
}
}
if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals -- after setFrame");
host.debug();
}
}
if (triggerGlobalLayoutListener) {
attachInfo.mRecomputeGlobalAttributes = false;
attachInfo.mTreeObserver.dispatchOnGlobalLayout();
}
/**
* 如果根视图包含一个 insets变化的监听者,
* 那么,需要执行监听者指定的动作,即调用
* attachInfo.mTreeObserver.dispatchOnComputeInternalInsets( )
* 。 执行完毕后,监听者将设置新的 insets 值,
* 如 果 insets发生了变化,则需要把这个变化报告给WmS,
* 即调用sWindowSession.setInsets()函数。
*/
if (computesInternalInsets) {
ViewTreeObserver.InternalInsetsInfo insets = attachInfo.mGivenInternalInsets;
final Rect givenContent = attachInfo.mGivenInternalInsets.contentInsets;
final Rect givenVisible = attachInfo.mGivenInternalInsets.visibleInsets;
givenContent.left = givenContent.top = givenContent.right
= givenContent.bottom = givenVisible.left = givenVisible.top
= givenVisible.right = givenVisible.bottom = 0;
attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
Rect contentInsets = insets.contentInsets;
Rect visibleInsets = insets.visibleInsets;
if (mTranslator != null) {
contentInsets = mTranslator.getTranslatedContentInsets(contentInsets);
visibleInsets = mTranslator.getTranslatedVisbileInsets(visibleInsets);
}
if (insetsPending || !mLastGivenInsets.equals(insets)) {
mLastGivenInsets.set(insets);
try {
sWindowSession.setInsets(mWindow, insets.mTouchableInsets,
contentInsets, visibleInsets);
} catch (RemoteException e) {
}
}
}
/**
* 8 如果mFirst为 true,则让该视图获得焦点,即调用mView.requestFocus()
*/
if (mFirst) {
// handle first focus request
if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: mView.hasFocus()="
+ mView.hasFocus());
if (mView != null) {
if (!mView.hasFocus()) {
mView.requestFocus(View.FOCUS_FORWARD);
mFocusedView = mRealFocusedView = mView.findFocus();
if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: requested focused view="
+ mFocusedView);
} else {
mRealFocusedView = mView.findFocus();
if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: existing focused view="
+ mRealFocusedView);
}
}
}
mFirst = false;
mWillDrawSoon = false;
mNewSurfaceNeeded = false;
mViewVisibility = viewVisibility;
/**
* 如 果 mAttachlnfo.mHasWindowFocus为 true,则意味
* 着该窗口已经是当前交互窗口,然后接着
* 调 用 WM.LP.mayUseInputMethod()判断
* 当前窗口是否会使用输入法,应用程序可以在
* AndroidManifest.xml指定是否应用窗口不
* 使用输入法,默认情况下都使用。如果是的话,则调用
* imm.onWindowFocus()通 知 InputMethodManager有了
* 新的窗口及视图,如果该视图可以获得焦点并可以
* 编辑,则输入法窗口将会显示出来。
*/
if (mAttachInfo.mHasWindowFocus) {
final boolean imTarget = WindowManager.LayoutParams
.mayUseInputMethod(mWindowAttributes.flags);
if (imTarget != mLastWasImTarget) {
mLastWasImTarget = imTarget;
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null && imTarget) {
imm.startGettingWindowFocus(mView);
imm.onWindowFocus(mView, mView.findFocus(),
mWindowAttributes.softInputMode,
!mHasHadWindowFocus, mWindowAttributes.flags);
}
}
}
/**
* 最后,就需要将所有的视图绘制到屏幕上,
* 开始真正的draw()流程。关 于draw()函数的内部执
* 行流程,见后面小节
*/
/**
* 1)判断没有取消绘制,并且不是newSurface,则调用draw()函数开始绘制。代码中使用cancelDraw
* 变量表示是否“取消绘制”,该值来源于TreeObserver的 dispatchPreDraw()回调,应用程序可以添加一
* 个 OnDispatchPreDraw接口对象,从而在开始绘制之前执行操作,并可以阻止重绘。newSurface变量的
* 含义是指,ViewRoot中创建了 Surface对象,但是该对象还没有被W mS分配真正的显存,ViewRoot
* 中是调用sWindowSession.relayoutWindow()为 该 Surface对象中分配真正的显存,在一般情况下,此处
* 的 newSurface 都是 false。
*
*/
boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw();
if (!cancelDraw && !newSurface) {
mFullRedrawNeeded = false;
draw(fullRedrawNeeded);
/**
* 如果mFirst为true,则绘制完毕后,需要向WmS报告绘制完成,即调用
* * sWindowSession.finishDrawing()
*/
if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0
|| mReportNextDraw) {
if (LOCAL_LOGV) {
Log.v(TAG, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
}
mReportNextDraw = false;
if (mSurfaceHolder != null && mSurface.isValid()) {
mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
if (c instanceof SurfaceHolder.Callback2) {
((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
mSurfaceHolder);
}
}
}
}
try {
sWindowSession.finishDrawing(mWindow);
} catch (RemoteException e) {
}
}
} else {
/**
* 如 果 cancelDraw或 者 newSurface条件不满足,则重新发起一次View遍历请求,以便当条件
* 满足后继续绘制
*/
// We were supposed to report when we are done drawing. Since we canceled the
// draw, remember it here.
if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
mReportNextDraw = true;
}
if (fullRedrawNeeded) {
mFullRedrawNeeded = true;
}
// Try again
scheduleTraversals();
}
}
performTraversals()
最新推荐文章于 2024-05-04 20:05:30 发布