赞
踩
在Android系统中,很多应用都需要根据具体情况来控制状态栏和导航栏的显示和隐藏,又或者将状态栏透明,实现诸如沉浸式、全面屏灯效果,而要实现这些效果,都离不开SystemUIVisibility属性。由于SystemUIVisibilityy属性主要用来控制系统状态栏和导航栏的行为,而状态栏和导航栏都属于SystemUI模块的StatusBar,所以SystemUIVisibility属性的消费者肯定包含StatusBar。另外当状态栏和导航栏发生变化的时候,窗口的布局一般也会跟着发生变化,这就意味着窗口管理者PhoneWindowManager肯定也要消费SystemUIVisibility属性。本篇文章我们就来具体分析一下和这个属性有关的代码。
1、为窗口设置SystemUIVisibility属性的方式有两种,一种是直接在窗口的WindowManager.LayoutParams对象的systemUiVisibility属性上进行设置,并通过WindowManager.updateViewLayout()方法使其生效。
frameworks/base/core/java/android/view/WindowManager.java
public interface WindowManager extends ViewManager {
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
//隐藏窗口的所有装饰,比如状态栏和导航栏
public static final int FLAG_FULLSCREEN = 0x00000400;
//控制窗口状态栏、导航栏的显示和隐藏
public int systemUiVisibility;
}
}
2、另一种是在一个已经显示在窗口上的控件中调用setSystemUiVisibility方法,传入如下属性。
frameworks/base/core/java/android/view/View.java
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
//隐藏导航栏
public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 0x00000002;
//隐藏状态栏
public static final int SYSTEM_UI_FLAG_FULLSCREEN = 0x00000004;
}
这种方式最终影响的其实是窗口的WindowManager.LayoutParams对象的subtreeSystemUiVisibility属性。
public interface WindowManager extends ViewManager {
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
//控制窗口状态栏、导航栏的显示和隐藏
public int subtreeSystemUiVisibility;
}
}
窗口的状态栏导航栏显示与否,最终其实是受以上两个属性共同影响的。接下来我们具体来分析一下View的setSystemUiVisibility方法是如何生效的。
1、View的setSystemUiVisibility方法如下所示。
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
int mSystemUiVisibility;
protected ViewParent mParent;
public void setSystemUiVisibility(int visibility) {
if (visibility != mSystemUiVisibility) {
mSystemUiVisibility = visibility;//保存SystemUIVisibility属性
if (mParent != null && mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
mParent.recomputeViewAttributes(this);//通知父控件子控件属性发生了变化
}
}
}
}
setSystemUiVisibility方法首先将属性赋值给mSystemUiVisibility,然后会调用父控件的recomputeViewAttributes方法,通知父控件子控件属性发生了变化。ViewParent是一个接口,在Android中有两个类实现了这个接口,它们分别是ViewGroup和ViewRootImpl。
2、ViewGroup和ViewRootImpl和recomputeViewAttributes方法相关的代码如下所示。
frameworks/base/core/java/android/view/View.java
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
@Override
public void recomputeViewAttributes(View child) {
if (mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
ViewParent parent = mParent;
if (parent != null) parent.recomputeViewAttributes(this);
}
}
}
frameworks/base/core/java/android/view/ViewRootImpl.java
public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks, AttachedSurfaceControl { final View.AttachInfo mAttachInfo; @Override public void recomputeViewAttributes(View child) { checkThread();//检测线程是不是UI线程 if (mView == child) { mAttachInfo.mRecomputeGlobalAttributes = true;//标记需要重新计算本地属性 if (!mWillDrawSoon) { scheduleTraversals();//进一步调用scheduleTraversals方法。 } } } }
结合Android 12系统源码_窗口管理(二)WindowManager对窗口的管理过程,我们知道包括Activity的跟布局DecorView在内的任何View,WindowManager在将它添加到窗口上的过程中,最终都会创建一个ViewRootImpl,并将View设置给ViewRootImpl,这样根View的父类就变成了ViewRootImpl。这就意味着不管任何子View调用recomputeViewAttributes方法,最终所触发的都是ViewRootImpl的recomputeViewAttributes,而ViewRootImpl会进一步调用scheduleTraversals方法。
3、ViewRootImpl和scheduleTraversals方法相关的代码如下所示。
public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks, AttachedSurfaceControl { final Choreographer mChoreographer;//编舞者 final TraversalRunnable mTraversalRunnable = new TraversalRunnable();//回调对象 void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); //等待系统垂直刷新同步信号,回调TraversalRunnable对象的run方法 mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } } final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal();//继续执行doTraversal } } void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } //执行performTraversals performTraversals(); if (mProfile) { Debug.stopMethodTracing(); mProfile = false; } } } }
scheduleTraversals方法会为编舞者对象设置回调,最终会等待系统垂直刷新同步信号,回调TraversalRunnable对象的run方法,该方法会调用doTraversal方法,然后进一步调用performTraversals方法。
4、ViewRootImpl的performTraversals方法代码逻辑非常多,这里只列出了我们需要关注的代码。
public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks, AttachedSurfaceControl { final IWindowSession mWindowSession;//和WMS通信的Binder对象,具体为Session对象 public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();//窗口属性 final View.AttachInfo mAttachInfo;//控件信息 private void performTraversals() { final View host = mView; if (host == null || !mAdded) { return; } if (mWaitForBlastSyncComplete) { mRequestedTraverseWhilePaused = true; return; } mIsInTraversal = true; mWillDrawSoon = true; boolean windowSizeMayChange = false; WindowManager.LayoutParams lp = mWindowAttributes;//将当前窗口的最新属性赋值给lp int desiredWindowWidth; int desiredWindowHeight; final int viewVisibility = getHostVisibility(); final boolean viewVisibilityChanged = !mFirst && (mViewVisibility != viewVisibility || mNewSurfaceNeeded || mAppVisibilityChanged); mAppVisibilityChanged = false; final boolean viewUserVisibilityChanged = !mFirst && ((mViewVisibility == View.VISIBLE) != (viewVisibility == View.VISIBLE)); WindowManager.LayoutParams params = null; ...代码省略... //收集mView的属性,判断是否需要更新params if (collectViewAttributes()) { params = lp; } ...代码省略... //此方法最终会触发WindowManagerService的relayoutWindow方法 relayoutWindow(params, viewVisibility, insetsPending); ...代码省略... //测量 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); ...代码省略... //布局 performLayout(lp, mWidth, mHeight); ...代码省略... //绘制 performDraw(); ...代码省略... } private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, boolean insetsPending) throws RemoteException { //调用IWindowSession的relayout方法,该方法最终会触发WindowManagerService的relayoutWindow方法 int relayoutResult = mWindowSession.relayout(mWindow, params, (int) (mView.getMeasuredWidth() * appScale + 0.5f), (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber, mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets, mTempControls, mSurfaceSize); } }
performTraversals首先调用collectViewAttributes方法收集所有子View的属性,然后调用relayoutWindow方法,该方法最终会触发WindowManagerService的relayoutWindow方法,然后回继续调用触发View测量的performMeasure方法,触发View布局的performLayout方法和触发View绘制的performDraw方法。
1、ViewRootImpl的collectViewAttributes方法是一个很关键的方法,此方法会重新计算最新的SystemUIVisibility属性。
public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks, AttachedSurfaceControl { public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();//窗口属性 final View.AttachInfo mAttachInfo;//控件信息 private boolean collectViewAttributes() { //判断是否需要重新计算本地属性 if (mAttachInfo.mRecomputeGlobalAttributes) { //Log.i(mTag, "Computing view hierarchy attributes!"); mAttachInfo.mRecomputeGlobalAttributes = false; boolean oldScreenOn = mAttachInfo.mKeepScreenOn; mAttachInfo.mKeepScreenOn = false; //清空已经存在的SystemUiVisibility属性 mAttachInfo.mSystemUiVisibility = 0; mAttachInfo.mHasSystemUiListeners = false; //重新获取窗口视图mView最新的的SystemUI属性,赋值给mAttachInfo mView.dispatchCollectViewAttributes(mAttachInfo, 0); ...代码省略... } return false; } }
collectViewAttributes首先会清空当前窗口视图mView已经存在的SystemUiVisibility属性,然后调用View的dispatchCollectViewAttributes方法重新获取最新的的SystemUiVisibility属性。
2、View的dispatchCollectViewAttributes方法如下所示。
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource { int mSystemUiVisibility; void dispatchCollectViewAttributes(AttachInfo attachInfo, int visibility) { performCollectViewAttributes(attachInfo, visibility); } void performCollectViewAttributes(AttachInfo attachInfo, int visibility) { if ((visibility & VISIBILITY_MASK) == VISIBLE) { if ((mViewFlags & KEEP_SCREEN_ON) == KEEP_SCREEN_ON) { attachInfo.mKeepScreenOn = true; } //将最新的systemuivisiblity赋予AttachInfo的mSystemUiVisibility 属性 attachInfo.mSystemUiVisibility |= mSystemUiVisibility; //设置最新的SystemUiVisibility监听对象,如果不为空,则将AttachInfo的mHasSystemUiListeners属性设置为true。 ListenerInfo li = mListenerInfo; if (li != null && li.mOnSystemUiVisibilityChangeListener != null) { attachInfo.mHasSystemUiListeners = true; } } } }
3、接着看ViewRootImpl的collectViewAttributes方法。
public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks, AttachedSurfaceControl { public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();//窗口属性 final View.AttachInfo mAttachInfo;//控件信息 private boolean collectViewAttributes() { //判断是否需要重新计算本地属性 if (mAttachInfo.mRecomputeGlobalAttributes) { ...代码省略... //移除被禁用的SystemUiVisibility属性 mAttachInfo.mSystemUiVisibility &= ~mAttachInfo.mDisabledSystemUiVisibility; //让params引用指向mWindowAttributes对象 WindowManager.LayoutParams params = mWindowAttributes; mAttachInfo.mSystemUiVisibility |= getImpliedSystemUiVisibility(params); mCompatibleVisibilityInfo.globalVisibility = (mCompatibleVisibilityInfo.globalVisibility & ~View.SYSTEM_UI_FLAG_LOW_PROFILE) | (mAttachInfo.mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE); dispatchDispatchSystemUiVisibilityChanged(mCompatibleVisibilityInfo); if (mAttachInfo.mKeepScreenOn != oldScreenOn || mAttachInfo.mSystemUiVisibility != params.subtreeSystemUiVisibility || mAttachInfo.mHasSystemUiListeners != params.hasSystemUiListeners) { applyKeepScreenOnFlag(params); //将重新获取的窗口视图mView的SystemUiVisibility保存到窗口的LayoutParams属性中 params.subtreeSystemUiVisibility = mAttachInfo.mSystemUiVisibility; params.hasSystemUiListeners = mAttachInfo.mHasSystemUiListeners; //调用View的dispatchWindowSystemUiVisiblityChanged方法 mView.dispatchWindowSystemUiVisiblityChanged(mAttachInfo.mSystemUiVisibility); return true; } } return false; } }
在重新获得mView的SystemUiVisibility属性之后,首先会从该属性中移除被禁用的SystemUiVisibility属性,然后让params引用指向mWindowAttributes对象,并将重新获取的保存在mAttachInfo对象中的SystemUiVisibility属性保存到当前窗口的LayoutParams属性中,最后会调用当前View的dispatchWindowSystemUiVisiblityChanged方法。
4、View的dispatchWindowSystemUiVisiblityChanged方法如下所示。
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
@Deprecated
public void dispatchWindowSystemUiVisiblityChanged(int visible) {
onWindowSystemUiVisibilityChanged(visible);//调用onWindowSystemUiVisibilityChanged方法
}
public void onWindowSystemUiVisibilityChanged(int visible) {
//默认为空实现
}
}
该方法会进一步调用onWindowSystemUiVisibilityChanged方法,onWindowSystemUiVisibilityChanged方法默认为空实现,但是如果当前mView为DecorView时则不同,DecorView实现了此方法。
1、DecorView的onWindowSystemUiVisibilityChanged方法如下所示。
frameworks/base/core/java/com/android/internal/policy/DecorView.java
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
@Override
public void onWindowSystemUiVisibilityChanged(int visible) {
//调用updateColorViews方法
updateColorViews(null /* insets */, true /* animate */);
updateDecorCaptionStatus(getResources().getConfiguration());
if (mStatusGuard != null && mStatusGuard.getVisibility() == VISIBLE) {
updateStatusGuardColor();
}
}
}
onWindowSystemUiVisibilityChanged方法会调用一个updateColorViews这个关键方法。
2、updateColorViews会调用updateColorViewInt方法更新导航栏和状态栏的背景颜色。
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks { WindowInsets updateColorViews(WindowInsets insets, boolean animate) { ...代码省略... //更新导航栏颜色,mNavigationColorViewState为导航栏的相关筛选条件 Log.d("SystemBar", "updateColorViews mNavigationColorViewState="+mNavigationColorViewState); updateColorViewInt(mNavigationColorViewState, calculateNavigationBarColor(appearance), mWindow.mNavigationBarDividerColor, navBarSize, navBarToRightEdge || navBarToLeftEdge, navBarToLeftEdge, 0 /* sideInset */, animate && !disallowAnimate, mForceWindowDrawsBarBackgrounds, controller); boolean oldDrawLegacy = mDrawLegacyNavigationBarBackground; mDrawLegacyNavigationBarBackground = mNavigationColorViewState.visible && (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0; if (oldDrawLegacy != mDrawLegacyNavigationBarBackground) { if (viewRoot != null) { viewRoot.requestInvalidateRootRenderNode(); } } ...代码省略... boolean statusBarNeedsRightInset = navBarToRightEdge && mNavigationColorViewState.present; boolean statusBarNeedsLeftInset = navBarToLeftEdge && mNavigationColorViewState.present; int statusBarSideInset = statusBarNeedsRightInset ? mLastRightInset : statusBarNeedsLeftInset ? mLastLeftInset : 0; int statusBarColor = calculateStatusBarColor(appearance); //更新状态栏颜色,mStatusColorViewState为状态栏的相关筛选条件 Log.d("SystemBar", "updateColorViews mStatusColorViewState="+mStatusColorViewState); updateColorViewInt(mStatusColorViewState, statusBarColor, 0, mLastTopInset, false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset, animate && !disallowAnimate, mForceWindowDrawsBarBackgrounds, controller); ...代码省略... } }
关于DecorView更新状态栏、导航栏背景颜色的过程,后续篇章会具体分析。
1、重新回到第二节第4步ViewRootImpl的performTraversals方法中。
public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks, AttachedSurfaceControl { final IWindowSession mWindowSession;//和WMS通信的Binder对象,具体为Session对象 public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();//窗口属性 final View.AttachInfo mAttachInfo;//控件信息 private void performTraversals() { ...代码省略... //收集mView的属性,判断是否需要更新params if (collectViewAttributes()) { params = lp; } ...代码省略... //此方法最终会触发WindowManagerService的relayoutWindow方法 relayoutWindow(params, viewVisibility, insetsPending); ...代码省略... //测量 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); ...代码省略... //布局 performLayout(lp, mWidth, mHeight); ...代码省略... //绘制 performDraw(); ...代码省略... } private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, boolean insetsPending) throws RemoteException { //调用IWindowSession的relayout方法,该方法最终会触发WindowManagerService的relayoutWindow方法 int relayoutResult = mWindowSession.relayout(mWindow, params, (int) (mView.getMeasuredWidth() * appScale + 0.5f), (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber, mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets, mTempControls, mSurfaceSize); } }
在调用collectViewAttributes获取最新的systemUIVisibiliy属性之后,会调用relayoutWindow方法,该方法进一步调用IWindowSession的relayout方法,IWindowSession的具体实现类为Session。
2、Session的relayout方法如下所示。
frameworks/base/services/core/java/com/android/server/wm/Session.java
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { final WindowManagerService mService; @Override public int relayout(IWindow window, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration, SurfaceControl outSurfaceControl, InsetsState outInsetsState, InsetsSourceControl[] outActiveControls, Point outSurfaceSize) { int res = mService.relayoutWindow(this, window, attrs, requestedWidth, requestedHeight, viewFlags, flags, frameNumber, outFrames, mergedConfiguration, outSurfaceControl, outInsetsState, outActiveControls, outSurfaceSize); return res; } }
relayout方法会进一步调用WindowManagerService的relayoutWindow方法。
3、WindowManagerService的relayoutWindow方法如下所示。
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs { public int relayoutWindow(Session session, IWindow client, LayoutParams attrs//窗口属性 , int requestedWidth, int requestedHeight, int viewVisibility//根View控件是否可见 , int flags, long frameNumber, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration, SurfaceControl outSurfaceControl, InsetsState outInsetsState, InsetsSourceControl[] outActiveControls, Point outSurfaceSize) { ...代码省略... final WindowState win = windowForClientLocked(session, client, false); if (win == null) { return 0; } final DisplayContent displayContent = win.getDisplayContent(); final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); ...代码省略... //是否应该重新布局 final boolean shouldRelayout = viewVisibility == View.VISIBLE && (win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING || win.mActivityRecord.isClientVisible()); ...代码省略... if (shouldRelayout) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1"); //标记 result = win.relayoutVisibleWindow(result); if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { //焦点可能变化了 focusMayChange = true; } if (win.mAttrs.type == TYPE_INPUT_METHOD && displayContent.mInputMethodWindow == null) { displayContent.setInputMethodWindowLocked(win); imMayMove = true; } win.adjustStartingWindowFlags(); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } ...代码省略... if (focusMayChange) { //relayoutWindow add by syl Log.d(TAG, "SystemBar: WindowManagerService_relayoutWindow"); if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) { imMayMove = false; } } ...代码省略... } }
relayoutWindow方法会调用一个关键方法updateFocusedWindowLocked。
4、WindowManagerService的updateFocusedWindowLocked方法如下所示。
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
RootWindowContainer mRoot;
//更新窗口焦点的变化
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
Log.d("SystemBar", "WindowManagerService: updateFocusedWindowLocked");
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
boolean changed = mRoot.updateFocusedWindowLocked(mode, updateInputWindows);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
return changed;
}
}
此方法会进一步调用RootWindowContainer的updateFocusedWindowLocked方法。
5、RootWindowContainer的updateFocusedWindowLocked方法如下所示。
/frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
class RootWindowContainer extends WindowContainer<DisplayContent> implements DisplayManager.DisplayListener { // 当窗口焦点发生变化的时候,更新窗口焦点的变化 boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) { mTopFocusedAppByProcess.clear(); boolean changed = false; int topFocusedDisplayId = INVALID_DISPLAY; for (int i = mChildren.size() - 1; i >= 0; --i) { //循环遍历DisplayContent的updateFocusedWindowLocked方法 final DisplayContent dc = mChildren.get(i); changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows, topFocusedDisplayId); final WindowState newFocus = dc.mCurrentFocus; if (newFocus != null) { final int pidOfNewFocus = newFocus.mSession.mPid; if (mTopFocusedAppByProcess.get(pidOfNewFocus) == null) { mTopFocusedAppByProcess.put(pidOfNewFocus, newFocus.mActivityRecord); } if (topFocusedDisplayId == INVALID_DISPLAY) { topFocusedDisplayId = dc.getDisplayId(); } } else if (topFocusedDisplayId == INVALID_DISPLAY && dc.mFocusedApp != null) { // The top-most display that has a focused app should still be the top focused // display even when the app window is not ready yet (process not attached or // window not added yet). topFocusedDisplayId = dc.getDisplayId(); } } if (topFocusedDisplayId == INVALID_DISPLAY) { topFocusedDisplayId = DEFAULT_DISPLAY; } if (mTopFocusedDisplayId != topFocusedDisplayId) { mTopFocusedDisplayId = topFocusedDisplayId; mWmService.mInputManager.setFocusedDisplay(topFocusedDisplayId); mWmService.mPolicy.setTopFocusedDisplay(topFocusedDisplayId); mWmService.mAccessibilityController.setFocusedDisplay(topFocusedDisplayId); ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, "New topFocusedDisplayId=%d", topFocusedDisplayId); } return changed; }
6、DisplayContent 的updateFocusedWindowLocked方法如下所示。
class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.DisplayContentInfo { //当窗口焦点发生变化的时候,更新窗口焦点的变化 boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows, int topFocusedDisplayId) { WindowState newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId); if (mCurrentFocus == newFocus) { return false; } boolean imWindowChanged = false; final WindowState imWindow = mInputMethodWindow; if (imWindow != null) { final WindowState prevTarget = mImeLayeringTarget; final WindowState newTarget = computeImeTarget(true /* updateImeTarget*/); imWindowChanged = prevTarget != newTarget; if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) { assignWindowLayers(false /* setLayoutNeeded */); } if (imWindowChanged) { mWmService.mWindowsChanged = true; setLayoutNeeded(); newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId); } } ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, "Changing focus from %s to %s displayId=%d Callers=%s", mCurrentFocus, newFocus, getDisplayId(), Debug.getCallers(4)); final WindowState oldFocus = mCurrentFocus; mCurrentFocus = newFocus; if (newFocus != null) { mWinAddedSinceNullFocus.clear(); mWinRemovedSinceNullFocus.clear(); if (newFocus.canReceiveKeys()) { // Displaying a window implicitly causes dispatching to be unpaused. // This is to protect against bugs if someone pauses dispatching but // forgets to resume. newFocus.mToken.paused = false; } } //触发DisplayPolicy的focusChangedLw方法。 getDisplayPolicy().focusChangedLw(oldFocus, newFocus); ...代码省略... } }
7、DisplayPolicy的focusChangedLw方法如下所示。
public class DisplayPolicy {
/**
* A new window has been focused.
*/
public void focusChangedLw(WindowState lastFocus, WindowState newFocus) {
//焦点发生了变化
mFocusedWindow = newFocus;
mLastFocusedWindow = lastFocus;
if (mDisplayContent.isDefaultDisplay) {
mService.mPolicy.onDefaultDisplayFocusChangedLw(newFocus);
}
//更新SystemBar的属性
updateSystemBarAttributes();
}
}
DisplayPolicy的focusChangedLw方法最终会调用updateSystemBarAttributes方法来刷新系统栏属性。
8、DisplayPolicy的updateSystemBarAttributes的方法如下所示。
public class DisplayPolicy { //更新SystemBar的属性 void updateSystemBarAttributes() { WindowState winCandidate = mFocusedWindow;//焦点窗口 if (winCandidate == null && mTopFullscreenOpaqueWindowState != null && (mTopFullscreenOpaqueWindowState.mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0) { //只有焦点窗口才能控制系统栏 winCandidate = mTopFullscreenOpaqueWindowState; } // If there is no window focused, there will be nobody to handle the events // anyway, so just hang on in whatever state we're in until things settle down. if (winCandidate == null) { return; } // The immersive mode confirmation should never affect the system bar visibility, otherwise // it will unhide the navigation bar and hide itself. if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) { // The immersive mode confirmation took the focus from mLastFocusedWindow which was // controlling the system ui visibility. So if mLastFocusedWindow can still receive // keys, we let it keep controlling the visibility. final boolean lastFocusCanReceiveKeys = (mLastFocusedWindow != null && mLastFocusedWindow.canReceiveKeys()); winCandidate = isKeyguardShowing() && !isKeyguardOccluded() ? mNotificationShade : lastFocusCanReceiveKeys ? mLastFocusedWindow : mTopFullscreenOpaqueWindowState; if (winCandidate == null) { return; } } final WindowState win = winCandidate; mSystemUiControllingWindow = win; final int displayId = getDisplayId();//获取屏幕设备id final int disableFlags = win.getDisableFlags();//获取窗口禁止的属性标记 //结合参数disableFlags重新更新系统栏窗口参数 final int opaqueAppearance = updateSystemBarsLw(win, disableFlags); final WindowState navColorWin = chooseNavigationColorWindowLw(mNavBarColorWindowCandidate, mDisplayContent.mInputMethodWindow, mNavigationBarPosition); final boolean isNavbarColorManagedByIme = navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow; final int appearance = updateLightNavigationBarLw(win.mAttrs.insetsFlags.appearance, navColorWin) | opaqueAppearance; final int behavior = win.mAttrs.insetsFlags.behavior; final String focusedApp = win.mAttrs.packageName; final boolean isFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR) || !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR); final AppearanceRegion[] appearanceRegions = new AppearanceRegion[mStatusBarColorWindows.size()]; for (int i = mStatusBarColorWindows.size() - 1; i >= 0; i--) { final WindowState windowState = mStatusBarColorWindows.get(i); appearanceRegions[i] = new AppearanceRegion( getStatusBarAppearance(windowState, windowState), new Rect(windowState.getFrame())); } if (mLastDisableFlags != disableFlags) { mLastDisableFlags = disableFlags; final String cause = win.toString(); callStatusBarSafely(statusBar -> statusBar.setDisableFlags(displayId, disableFlags, cause)); } if (mLastAppearance == appearance && mLastBehavior == behavior && mRequestedVisibilities.equals(win.getRequestedVisibilities()) && Objects.equals(mFocusedApp, focusedApp) && mLastFocusIsFullscreen == isFullscreen && Arrays.equals(mLastStatusBarAppearanceRegions, appearanceRegions)) { return; } if (mDisplayContent.isDefaultDisplay && mLastFocusIsFullscreen != isFullscreen && ((mLastAppearance ^ appearance) & APPEARANCE_LOW_PROFILE_BARS) != 0) { mService.mInputManager.setSystemUiLightsOut( isFullscreen || (appearance & APPEARANCE_LOW_PROFILE_BARS) != 0); } final InsetsVisibilities requestedVisibilities = new InsetsVisibilities(win.getRequestedVisibilities()); mLastAppearance = appearance; mLastBehavior = behavior; mRequestedVisibilities = requestedVisibilities; mFocusedApp = focusedApp; mLastFocusIsFullscreen = isFullscreen; mLastStatusBarAppearanceRegions = appearanceRegions; //调用状态栏管理服务StatusBarManagerService的onSystemBarAttributesChanged callStatusBarSafely(statusBar -> statusBar.onSystemBarAttributesChanged(displayId, appearance, appearanceRegions, isNavbarColorManagedByIme, behavior, requestedVisibilities, focusedApp)); } }
updateSystemBarAttributes方法首先获取当前的焦点窗口mFocusedWindow,将该窗口赋值给winCandidate,并判断该窗口是否可以操控系统栏,如果不允许会直接返回;如果允许,则会结合winCandidate的属性调用updateSystemBarsLw方法,来更新系统栏参数,之后还会调用状态栏管理服务StatusBarManagerService的onSystemBarAttributesChanged,而此回调方法最终会回传给系统状态栏和导航栏,使得新的SystemUIVisibility生效。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。