前言

NavigationBar 和 StatusBar 都属于 SystemBar,也叫做 decor,就是说给 App 装饰的意思。一般的 window 的布局是在 PhoneWindowManager 的 layoutWindowLw() 方法中,而 SystemBar 是在 beginLayoutLw() 方法中布局。

当前最上层的 Activity 可以修改 SystemBar 的 visibility,可以调用 View#setSystemUiVisibility() 方法,系统也有一些针对 SystemBar visibility 的策略。最终的 visibility 保存在 PhoneWindowManager 中的 mLastSystemUiFlags 变量中。

一、简单认识DisplayFrames

在分析NaivgationBar和StatusBar对应的窗口布局前,需要我们先来简单认识Android中的DisplayFrames对象。

frameworks/base/services/core/java/com/android/server/wm/DisplayFrames.java

public class DisplayFrames {//物理屏幕相关的设备idpublic final int mDisplayId;/*** The current size of the screen; really; extends into the overscan area of the screen and* doesn't account for any system elements like the status bar.*///当前的屏幕大小,包括过扫描区域。过扫描区域在输出到 TV 时会用到,// 对于移动设备来说 mOverscan 大小就是物理设备的大小 (0,0)-(dw,dh)。public final Rect mOverscan = new Rect();/*** The current visible size of the screen; really; (ir)regardless of whether the status bar can* be hidden but not extending into the overscan area.*///当前可见的屏幕大小,其实就是 (0,0)-(dw,dh)。public final Rect mUnrestricted = new Rect();/** Like mOverscan*, but allowed to move into the overscan region where appropriate. *///是应用可显示的区域,包含 StatusBar 的区域,不包含 NavigationBar 区域。public final Rect mRestrictedOverscan = new Rect();/*** The current size of the screen; these may be different than (0,0)-(dw,dh) if the status bar* can't be hidden; in that case it effectively carves out that area of the display from all* other windows.*///一般情况下 mRestrictedOverscan 与 mRestricted 相同public final Rect mRestricted = new Rect();/*** During layout, the current screen borders accounting for any currently visible system UI* elements.*///是布局过程中,当前画面的边界,包含 Translucent(半透明)区域。一般情况下 NavigationBar 区域不是 Translucent,而 StatusBar 是 Translucent。public final Rect mSystem = new Rect();/** For applications requesting stable content insets, these are them. *///是应用窗口的显示区域,不包含 StatusBar 和 NavigationBar。public final Rect mStable = new Rect();/*** For applications requesting stable content insets but have also set the fullscreen window* flag, these are the stable dimensions without the status bar.*///是当Activity设置Fullscreen flag 时候的窗口显示区域,这时 StatusBar 会隐藏。public final Rect mStableFullscreen = new Rect();/*** During layout, the current screen borders with all outer decoration (status bar, input method* dock) accounted for.*/// 是布局的时候除去外部装饰的窗口(例如 StatusBar 和输入法窗口)。public final Rect mCurrent = new Rect();/*** During layout, the frame in which content should be displayed to the user, accounting for all* screen decoration except for any space they deem as available for other content. This is* usually the same as mCurrent*, but may be larger if the screen decor has supplied content* insets.*/// 是当前应该给用户显示的窗口,通常与 mCurrent 相同。当装饰窗口提供内容插入的时候,有可能比 mCurrent 更大。public final Rect mContent = new Rect();/*** During layout, the frame in which voice content should be displayed to the user, accounting* for all screen decoration except for any space they deem as available for other content.*///mVoiceContent 通常与 mContent 相同。public final Rect mVoiceContent = new Rect();/** During layout, the current screen borders along which input method windows are placed. *///mDock 是输入法布局时的边界。public final Rect mDock = new Rect();/** The display cutout used for layout (after rotation) *///用于刘海屏布局的剪刀工具,Android 9.0 新加入的。@NonNull public WmDisplayCutout mDisplayCutout = WmDisplayCutout.NO_CUTOUT;/** The cutout as supplied by display info *///用于刘海屏布局的剪刀工具,Android 9.0 新加入的。@NonNull public WmDisplayCutout mDisplayInfoCutout = WmDisplayCutout.NO_CUTOUT;/*** During layout, the frame that is display-cutout safe, i.e. that does not intersect with it.*///是在刘海屏上可以安全显示的区域,即这个区域与刘海区域没有交集。public final Rect mDisplayCutoutSafe = new Rect();private final Rect mDisplayInfoOverscan = new Rect();private final Rect mRotatedDisplayInfoOverscan = new Rect();public int mDisplayWidth;//物理屏幕宽度public int mDisplayHeight;//物理屏幕高度public int mRotation;//屏幕旋转角度public DisplayFrames(int displayId, DisplayInfo info, WmDisplayCutout displayCutout) {mDisplayId = displayId;onDisplayInfoUpdated(info, displayCutout);}public void onDisplayInfoUpdated(DisplayInfo info, WmDisplayCutout displayCutout) {mDisplayWidth = info.logicalWidth;mDisplayHeight = info.logicalHeight;mRotation = info.rotation;mDisplayInfoOverscan.set(info.overscanLeft, info.overscanTop, info.overscanRight, info.overscanBottom);mDisplayInfoCutout = displayCutout != null ? displayCutout : WmDisplayCutout.NO_CUTOUT;}public void onBeginLayout() {switch (mRotation) {case ROTATION_90:mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.top;mRotatedDisplayInfoOverscan.top = mDisplayInfoOverscan.right;mRotatedDisplayInfoOverscan.right = mDisplayInfoOverscan.bottom;mRotatedDisplayInfoOverscan.bottom = mDisplayInfoOverscan.left;break;case ROTATION_180:mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.right;mRotatedDisplayInfoOverscan.top = mDisplayInfoOverscan.bottom;mRotatedDisplayInfoOverscan.right = mDisplayInfoOverscan.left;mRotatedDisplayInfoOverscan.bottom = mDisplayInfoOverscan.top;break;case ROTATION_270:mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.bottom;mRotatedDisplayInfoOverscan.top = mDisplayInfoOverscan.left;mRotatedDisplayInfoOverscan.right = mDisplayInfoOverscan.top;mRotatedDisplayInfoOverscan.bottom = mDisplayInfoOverscan.right;break;default:mRotatedDisplayInfoOverscan.set(mDisplayInfoOverscan);break;}mRestrictedOverscan.set(0, 0, mDisplayWidth, mDisplayHeight);mOverscan.set(mRestrictedOverscan);mSystem.set(mRestrictedOverscan);mUnrestricted.set(mRotatedDisplayInfoOverscan);mUnrestricted.right = mDisplayWidth - mUnrestricted.right;mUnrestricted.bottom = mDisplayHeight - mUnrestricted.bottom;mRestricted.set(mUnrestricted);mDock.set(mUnrestricted);mContent.set(mUnrestricted);mVoiceContent.set(mUnrestricted);mStable.set(mUnrestricted);mStableFullscreen.set(mUnrestricted);mCurrent.set(mUnrestricted);mDisplayCutout = mDisplayInfoCutout;mDisplayCutoutSafe.set(Integer.MIN_VALUE, Integer.MIN_VALUE,Integer.MAX_VALUE, Integer.MAX_VALUE);if (!mDisplayCutout.getDisplayCutout().isEmpty()) {final DisplayCutout c = mDisplayCutout.getDisplayCutout();if (c.getSafeInsetLeft() > 0) {mDisplayCutoutSafe.left = mRestrictedOverscan.left + c.getSafeInsetLeft();}if (c.getSafeInsetTop() > 0) {mDisplayCutoutSafe.top = mRestrictedOverscan.top + c.getSafeInsetTop();}if (c.getSafeInsetRight() > 0) {mDisplayCutoutSafe.right = mRestrictedOverscan.right - c.getSafeInsetRight();}if (c.getSafeInsetBottom() > 0) {mDisplayCutoutSafe.bottom = mRestrictedOverscan.bottom - c.getSafeInsetBottom();}}}}

二、构建SystemBar对应的视图窗口区域坐标对象

1、系统主要是在PhoneWindowManager的beginLayoutLw() 方法中构建SystemBar对应的视图窗口区域坐标对象的。

frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

public class PhoneWindowManager implements WindowManagerPolicy {@Overridepublic void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {displayFrames.onBeginLayout();// TODO(multi-display): This doesn't seem right...Maybe only apply to default display?mSystemGestures.screenWidth = displayFrames.mUnrestricted.width();mSystemGestures.screenHeight = displayFrames.mUnrestricted.height();mDockLayer = 0x10000000;mStatusBarLayer = -1;// start with the current dock rect, which will be (0,0,displayWidth,displayHeight)final Rect pf = mTmpParentFrame;final Rect df = mTmpDisplayFrame;final Rect of = mTmpOverscanFrame;final Rect vf = mTmpVisibleFrame;final Rect dcf = mTmpDecorFrame;vf.set(displayFrames.mDock);of.set(displayFrames.mDock);df.set(displayFrames.mDock);pf.set(displayFrames.mDock);dcf.setEmpty();  // Decor frame N/A for system bars.if (displayFrames.mDisplayId == DEFAULT_DISPLAY) {// For purposes of putting out fake window up to steal focus, we will// drive nav being hidden only by whether it is requested.//获取窗口systemui的标记类型final int sysui = mLastSystemUiFlags;//navigationBar是否可见boolean navVisible = (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;//navigationBar是否是半透明的boolean navTranslucent = (sysui & (View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT)) != 0;boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0;boolean immersiveSticky = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;//navigationBar是否允许隐藏boolean navAllowedHidden = immersive || immersiveSticky;navTranslucent &= !immersiveSticky;  // transient trumps translucentboolean isKeyguardShowing = isStatusBarKeyguard() && !mKeyguardOccluded;if (!isKeyguardShowing) {navTranslucent &= areTranslucentBarsAllowed();}boolean statusBarExpandedNotKeyguard = !isKeyguardShowing && mStatusBar != null&& mStatusBar.getAttrs().height == MATCH_PARENT&& mStatusBar.getAttrs().width == MATCH_PARENT;// When the navigation bar isn't visible, we put up a fake input window to catch all// touch events. This way we can detect when the user presses anywhere to bring back the// nav bar and ensure the application doesn't see the event.if (navVisible || navAllowedHidden) {if (mInputConsumer != null) {mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer));mInputConsumer = null;}} else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) {mInputConsumer = mWindowManagerFuncs.createInputConsumer(mHandler.getLooper(),INPUT_CONSUMER_NAVIGATION,(channel, looper) -> new HideNavInputEventReceiver(channel, looper));// As long as mInputConsumer is active, hover events are not dispatched to the app// and the pointer icon is likely to become stale. Hide it to avoid confusion.InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL);}// For purposes of positioning and showing the nav bar, if we have decided that it can't// be hidden (because of the screen aspect ratio), then take that into account.navVisible |= !canHideNavigationBar();//调用layoutNavigationBar构建导航栏boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, dcf,navVisible, navTranslucent, navAllowedHidden, statusBarExpandedNotKeyguard);if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock);//调用layoutStatusBar构建状态栏updateSysUiVisibility |= layoutStatusBar(displayFrames, pf, df, of, vf, dcf, sysui, isKeyguardShowing);if (updateSysUiVisibility) {updateSystemUiVisibilityLw();}}layoutScreenDecorWindows(displayFrames, pf, df, dcf);if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) {// Make sure that the zone we're avoiding for the cutout is at least as tall as the// status bar; otherwise fullscreen apps will end up cutting halfway into the status// bar.displayFrames.mDisplayCutoutSafe.top = Math.max(displayFrames.mDisplayCutoutSafe.top,displayFrames.mStable.top);}}}

beginLayoutLw方法中首先会判断NavigationBar是否可见以及是否半透明,然后调用layoutNavigationBar方法和layoutStatusBar方法构建NavigationBar和StatusBar所对应的视图窗口区域坐标。

三、导航栏视图窗口区域坐标对象的构建

构建导航栏视图窗口区域坐标的layoutNavigationBar方法r如下所示。

public class PhoneWindowManager implements WindowManagerPolicy {//导航栏视图对应的窗口状态WindowState mNavigationBar = null;//导航栏视图对应的窗口区域坐标static final Rect mTmpNavigationFrame = new Rect();//导航栏视图控制器private final BarController mNavigationBarController = new BarController("NavigationBar",View.NAVIGATION_BAR_TRANSIENT,View.NAVIGATION_BAR_UNHIDE,View.NAVIGATION_BAR_TRANSLUCENT,StatusBarManager.WINDOW_NAVIGATION_BAR,FLAG_TRANSLUCENT_NAVIGATION,View.NAVIGATION_BAR_TRANSPARENT);//构建导航栏视图对应的窗口区域坐标private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, Rect dcf,boolean navVisible, boolean navTranslucent, boolean navAllowedHidden,boolean statusBarExpandedNotKeyguard) {if (mNavigationBar == null) {return false;}boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();//根据屏幕旋转角度,我们需要为导航栏视图设置相对应的位置和大小final int rotation = displayFrames.mRotation;final int displayHeight = displayFrames.mDisplayHeight;final int displayWidth = displayFrames.mDisplayWidth;final Rect dockFrame = displayFrames.mDock;//获取导航栏的位置mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation);//cutoutSafeUnrestricted是安全的窗口(Android9针对刘海平新增的),当没有Overscan的时候与mUnrestricted相同//即cutoutSafeUnrestricted.bottom的值与DisplayFrames.mDisplayHeight值相同。final Rect cutoutSafeUnrestricted = mTmpRect;cutoutSafeUnrestricted.set(displayFrames.mUnrestricted);cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe);//导航栏在底部if (mNavigationBarPosition == NAV_BAR_BOTTOM) {//计算导航栏的左上角的Y坐标final int top = cutoutSafeUnrestricted.bottom - getNavigationBarHeight(rotation, uiMode);//mTmpNavigationFrame就是NavigationBar所对应的窗口区域。mTmpNavigationFrame.set(0, top, displayWidth, displayFrames.mUnrestricted.bottom);//mStable对应应用窗口的显示区域,mStableFullscreen对应应用窗口全屏的显示区域,这里的设置使得应用窗口正常状态和全屏的时候都在导航栏的上方displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top;if (transientNavBarShowing) {mNavigationBarController.setBarShowingLw(true);} else if (navVisible) {//如果NavigationBar可见的话,更新dockFrame、mRestricted、mRestrictedOverscan的bottom值mNavigationBarController.setBarShowingLw(true);dockFrame.bottom = displayFrames.mRestricted.bottom = displayFrames.mRestrictedOverscan.bottom = top;} else {// We currently want to hide the navigation UI - unless we expanded the status bar.mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);}if (navVisible && !navTranslucent && !navAllowedHidden&& !mNavigationBar.isAnimatingLw()&& !mNavigationBarController.wasRecentlyTranslucent()) {// If the opaque nav bar is currently requested to be visible and not in the process// of animating on or off, then we can tell the app that it is covered by it.displayFrames.mSystem.bottom = top;}} else if (mNavigationBarPosition == NAV_BAR_RIGHT) {...代码省略...} else if (mNavigationBarPosition == NAV_BAR_LEFT) {...代码省略...}//使用dockFrame的参数去更新mCurrent,mVoiceContent,mContentdisplayFrames.mCurrent.set(dockFrame);displayFrames.mVoiceContent.set(dockFrame);displayFrames.mContent.set(dockFrame);mStatusBarLayer = mNavigationBar.getSurfaceLayer();//计算NavigationBar视图所对应的窗口坐标对应contentFrame的大小mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe, mTmpNavigationFrame, dcf,mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe,displayFrames.mDisplayCutout, false /* parentFrameWasClippedByDisplayCutout */);//将NavigationBar的contentFrame给到mNavigationBarController。mNavigationBarController.setContentFrame(mNavigationBar.getContentFrameLw());if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);return mNavigationBarController.checkHiddenLw();}//获取导航栏的位置private int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) {if (mNavigationBarCanMove && displayWidth > displayHeight) {if (displayRotation == Surface.ROTATION_270) {return NAV_BAR_LEFT;} else {return NAV_BAR_RIGHT;}}return NAV_BAR_BOTTOM;}
}

对以上代码做个简单总结:

1)layoutNavigationBar方法首先会调用 navigationBarPosition()方法返回 NavigationBar 的位置,根据返回的位置的不同所执行的布局方式也会不同。

2)我们这里以布局在下方为例,计算导航栏NavigationBar的左上角Y坐标top,top的值等于cutoutSafeUnrestricted.bottom减去NavigationBar高度。cutoutSafeUnrestricted是安全的窗口(Android 9 针对刘海平新增的),当没有 Overscan 的时候与 mUnrestricted 相同,即 cutoutSafeUnrestricted.bottom 的值与 DisplayFrames.mDisplayHeight 值相同。

3)在得到导航栏NavigationBar的左上角Y坐标top之后,会将导航栏相关的坐标给到mTmpNavigationFrame。mTmpNavigationFrame就是导航栏NavigationBar视图所对应的窗口区域。之后会更新应用程序安全显示区域mStable和应用程序全屏安全显示区域mStableFullscreen的最下方Y坐标bottom值为top。这就是为什么NavigationBar视图占用应用显示区域的原因。

4)如果 NavigationBar 可见的话则更新 dockFrame、mRestricted、mRestrictedOverscan 的 bottom 值。然后用 dockFrame 去更新 mCurrent、mVoiceContent、mContent。最后调用WindowState的computeFrameLw() 方法计算NavigationBar视图所对应的窗口坐标contentFrame的大小,并将结果给到mNavigationBarController对象。

四、状态栏视图窗口区域坐标对象的构建

1、构建状态栏视图窗口区域坐标的layoutStatusBar方法r如下所示。

public class PhoneWindowManager implements WindowManagerPolicy {//状态栏视图所对应的窗口状态WindowState mStatusBar = null;private static final Rect mTmpRect = new Rect();private final StatusBarController mStatusBarController = new StatusBarController();//构建状态栏视图对应的窗口区域坐标private boolean layoutStatusBar(DisplayFrames displayFrames, Rect pf, Rect df, Rect of, Rect vf,Rect dcf, int sysui, boolean isKeyguardShowing) {// decide where the status bar goes ahead of timeif (mStatusBar == null) {return false;}// apply any navigation bar insetsof.set(displayFrames.mUnrestricted);df.set(displayFrames.mUnrestricted);pf.set(displayFrames.mUnrestricted);vf.set(displayFrames.mStable);mStatusBarLayer = mStatusBar.getSurfaceLayer();//计算StatusBar的mContentFrame的大小mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */,vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */,dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */,displayFrames.mDisplayCutout, false /* parentFrameWasClippedByDisplayCutout */);//因为StatusBar默认显示在顶部的,所以修改应用程序安全显示区域mStable.top的值。displayFrames.mStable.top = displayFrames.mUnrestricted.top+ mStatusBarHeightForRotation[displayFrames.mRotation];// Make sure the status bar covers the entire cutout heightdisplayFrames.mStable.top = Math.max(displayFrames.mStable.top,displayFrames.mDisplayCutoutSafe.top);//计算 StatusBar 的可显示区域,并设置给mStatusBarController。mTmpRect.set(mStatusBar.getContentFrameLw());mTmpRect.intersect(displayFrames.mDisplayCutoutSafe);mTmpRect.top = mStatusBar.getContentFrameLw().top;  // Ignore top display cutout insetmTmpRect.bottom = displayFrames.mStable.top;  // Use collapsed status bar sizemStatusBarController.setContentFrame(mTmpRect);//判断 StatusBar 是否是短暂显示的(Transient)或是半透明的(Translucent)。其中 sysui 就是 mLastSystemUiFlags。boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;boolean statusBarTranslucent = (sysui& (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0;if (!isKeyguardShowing) {statusBarTranslucent &= areTranslucentBarsAllowed();}if (mStatusBar.isVisibleLw() && !statusBarTransient) {//如果 StatusBar 是可见的,且不是暂时显示的,则修改 displayFrames 的mDock、mContent、mVoiceContent、mCurrent,其实就是除去 StatusBar的窗口大小。final Rect dockFrame = displayFrames.mDock;dockFrame.top = displayFrames.mStable.top;displayFrames.mContent.set(dockFrame);displayFrames.mVoiceContent.set(dockFrame);displayFrames.mCurrent.set(dockFrame);if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " + String.format("dock=%s content=%s cur=%s", dockFrame.toString(),displayFrames.mContent.toString(), displayFrames.mCurrent.toString()));if (!mStatusBar.isAnimatingLw() && !statusBarTranslucent&& !mStatusBarController.wasRecentlyTranslucent()) {//如果 StatusBar 是不透明的,则修改 mSystem.top 值为 mStable.top 值,这时 Activity 使用 @android:style/Theme.NoTitleBar.Fullscreen 也不会隐藏 StatusBar。displayFrames.mSystem.top = displayFrames.mStable.top;}}return mStatusBarController.checkHiddenLw();
}}

1)首先设定 of /* overscanFrame /、df / displayFrame /、pf / parentFrame /、vf / visibleFrame */,其中 of、df、pf 设定为 displayFrames.mUnrestricted,即屏幕大小。vf 设定为 displayFrames.mStable,前面有提到mStable 的大小在调用 layoutNavigationBar() 方法后变成了除去NavigationBar的窗口,即应用程序安全显示区域。

2)然后调用 mStatusBar.computeFrameLw() 方法计算 StatusBar 的 mContentFrame 大小。mContentFrame 在 IME (输入法)不存在时与 mDecorFrame 相同,IME 存在时是 mDecorFrame 除去 IME 窗口的大小。

3)然后修改 displayFrames.mStable,即应用的窗口。因为 StatusBar 默认显示在顶部的,所以修改 mStable.top 值。计算 StatusBar 的可显示区域,并将计算结果设置到mStatusBarController中。

4)判断 StatusBar 是否是短暂显示的(Transient)或是半透明的(Translucent)。其中 sysui 就是 mLastSystemUiFlags。如果 StatusBar 是可见的,且不是暂时显示的,则修改 displayFrames 的 mDock、mContent、mVoiceContent、mCurrent,其实就是除去 StatusBar 的窗口大小。还有,如果 StatusBar 是不透明的,则修改 mSystem.top 值为 mStable.top 值,这时 Activity 使用 @android:style/Theme.NoTitleBar.Fullscreen 也不会隐藏 StatusBar。

Android 9.0系统源码_SystemUI(九)PhoneWindowManager构建状态栏和导航栏视图窗口区域坐标的流程解析相关推荐

  1. Android 9.0系统源码_SystemUI(一)SystemUI的启动流程

    一.SystemUI 介绍 1.初步认识SystemUI Android 的 SystemUI 其实就是 Android 的系统界面,它包括了界面上方的状态栏 status bar,下方的导航栏Nav ...

  2. Android 9.0系统源码_SystemUI(四)通知图标控制器

    前言 上一篇我们具体分析了状态栏上状态图标,例如 wifi.蓝牙等图标的控制流程,本篇文章我们继续来分析下状态栏上通知图标的控制流程.主要分析当一个新通知来临时,新通知的图标是如何一步步显示到状态栏上 ...

  3. Android 9.0系统源码_SystemUI(二)StatusBar系统状态栏的创建流程

    前言 上一篇我们具体分析了SystemUI的启动流程,在SystemServer的startOtherServices方法中,会启动SystemUIService服务,SystemUIService服 ...

  4. Android 9.0系统源码_SystemUI(六)滑动锁屏的创建

    一.前言 前面几篇文章大致介绍了SystemUI的两个模块,StatusBar和QuickSetting,这篇文章开始分析Keyguard模块. 对于锁屏呢,需要有个基本认知,它分为两类,一是滑动锁屏 ...

  5. Android 9.0系统源码_SystemUI(三)系统状态图标控制

    前言 上一篇我们具体分析了系统状态栏StatusBar的创建过程,其中状态栏视图就存储在CollapsedStatusBarFragment中,这个视图被添加到id为status_bar_contai ...

  6. android 系统源码调试 局部变量值_如何方便快速的整编Android 9.0系统源码?

    点击上方"刘望舒",选择"星标" 多点在看,就是真爱! 作者 :  刘望舒  |  来源 :刘望舒的博客地址:http://liuwangshu.cn/fram ...

  7. Android 8.0系统源码分析--开篇

    个人分类: Android框架总结Android源码解析android framework 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/sinat ...

  8. Android 8.0系统源码分析--Camera processCaptureResult结果回传源码分析

    相机,从上到下概览一下,真是太大了,上面的APP->Framework->CameraServer->CameraHAL,HAL进程中Pipeline.接各种算法的Node.再往下的 ...

  9. Ubuntu16.04编译Android 6.0系统源码过程简要记录总结

    一,安装VMware Workstation,百度网盘下载(内含注册机) 链接: https://pan.baidu.com/s/1wz4hdNQBikTvyUMNokSVYg 提取码: yed7 V ...

最新文章

  1. 【NOIP2013模拟】粉刷匠 题解代码
  2. 密码学专题 随机数文件
  3. saiku+kettle整合(六)olap操作
  4. MySQL 对查询结果进行排序
  5. 用5毛特效,让1000万人上瘾!这群乡村大妈,打了所有流量明星的脸
  6. FastDFS+Nginx+Module
  7. 虚拟机安装教程win10_Parallels Desktop如何安装windowns系统?PD虚拟机安装win10系统详细教程
  8. yshon对讲机如何调频率_对讲机频率怎么调?四步教你给对讲机调频
  9. App下载的视频导进电脑中生成.mp4文件的方法
  10. Java服务端接入苹果内购。实现票据二次校验、自动续期订阅
  11. 数据库MySQL(基础六)
  12. Word学习笔记:P6-文档封面、页眉、页脚设置
  13. 打开Word提示:Office已阻止访问以下嵌入对象,以便保护你的安全解决方法
  14. 互联网公司如何管理研发团队
  15. 单片机、嵌入式错综复杂的关系分析
  16. [综述] 细粒度图像分析2019
  17. 如何实现企业全链路协同,实现企业业绩增长
  18. 人事办公考勤工资管理系统(ssm,mysql)
  19. 如果你用String.spit(“|”)
  20. 【ROS理论与实践-赵虚左老师】Chap2 ROS通信机制

热门文章

  1. anki公司即将出品Vector家庭机器人(正在预售)
  2. win10电脑提示bootmgr is missing的解决方法
  3. 古文观止卷七_歸去來辭_陶淵明
  4. 偷菜的革命——献给所有忙于偷菜、乐于偷菜和疲于偷菜的您!
  5. 项目管理PMP学习之10大知识领域
  6. 为什么都不想去中科创达_排了10000桌的超级文和友,我这辈子都不想再去了
  7. 希捷 混合硬盘 装linux,实际应用测试全文总结
  8. 简书张帅Android,AndroidAnimationExercise
  9. 一读《清晰思考的艺术》-----溺水狗
  10. 下一步学习计划-----参考培训班课程