WMS->窗口管理statusbar和navbar管理

要想读懂窗口布局管理,必须了解status bar 和nav bar ,要了解这两个bar 更要了解相关的flags,下面就会这些flags做一些说明

View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR  //高亮状态栏

View.STATUS_BAR_TRANSLUCENT //半透明状态栏
View.STATUS_BAR_TRANSPARENT //透明状态栏,一般指定WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS系统会设置成全透明

View.SYSTEM_UI_FLAG_FULLSCREEN // 全屏显示,隐藏状态栏和状态栏
View.STATUS_BAR_UNHIDE     // 显示状态栏,用于传递给systemui处理

View.NAVIGATION_BAR_TRANSPARENT //半透明导航栏
View.NAVIGATION_BAR_TRANSLUCENT //透明导航栏,一般指定WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS系统会设置成全透明

View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // 隐藏导航栏
View.NAVIGATION_BAR_UNHIDE      // 显示状态栏,传递给systemui处理

View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY //身临其境的感觉,和SYSTEM_UI_FLAG_LIGHT_STATUS_BAR一起使用会隐藏状态栏和导航栏,从上面/下面滑出状态栏和导航栏,过几秒自动消失
View.SYSTEM_UI_FLAG_IMMERSIVE //身临其境的感觉,自动隐藏状态栏和导航栏,出上部/下部滑动状态栏出现,不自动隐藏


View.STATUS_BAR_TRANSIENT  //进入瞬态,状态栏出来和隐藏的过程
View.NAVIGATION_BAR_TRANSIENT //进入瞬态,导航栏出来和隐藏的过程



WindowManager.LayoutParams.FLAG_FULLSCREEN //全屏显示,隐藏状态栏
WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS //导航栏状态栏透明,客户端渲染状态栏导航栏背景
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND //强制导航栏状态栏透明,客户端渲染状态栏导航栏背景
WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR  //当此窗口到达顶部的时候保持前一个窗口的透明状态

WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS //指定半透明status bar 
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION //指定半透明nav bar 

WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND 强制渲染背景色

下面这几个状态用于status bar 和nav bar 切换时候的瞬态过程
private static final int TRANSIENT_BAR_NONE = 0; //无任何状态,当隐藏完成时候设置
private static final int TRANSIENT_BAR_SHOW_REQUESTED = 1; // 请求显示
private static final int TRANSIENT_BAR_SHOWING = 2; //正在显示的过程
private static final int TRANSIENT_BAR_HIDING = 3; //正在隐藏的过程,隐藏完成window变成不可见,设置TRANSIENT_BAR_NONE

还要记住,分屏模式不允许隐藏bar

另外管理的还要注意几个状态,包括状态栏展开,锁屏和锁屏被阻断,以及正常的全屏activity在前台的几种情况。
状态栏管理在wms这端的核心函数是PhoneWindowManager中的updateSystemUiVisibilityLw()函数

private int updateSystemUiVisibilityLw() {
        // 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.
        //1 获取焦点的window也就是能接收input事件的window
        WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow
                : mTopFullscreenOpaqueWindowState;
        if (winCandidate == null) {
            return 0;
        }
        //2 如果当前焦点的window是ImmersiveModeConfirmation弹出的window不做处理,直接返回
        if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) {
            // The immersive mode confirmation should never affect the system bar visibility,
            // otherwise it will unhide the navigation bar and hide itself.
            winCandidate = isStatusBarKeyguard() ? mStatusBar : mTopFullscreenOpaqueWindowState;
            if (winCandidate == null) {
                return 0;
            }
        }
        final WindowState win = winCandidate;
        //3 焦点window是keygurd,但是keygurd被阻断,不处理直接返回
        if ((win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 && mHideLockScreen == true) {
            // We are updating at a point where the keyguard has gotten
            // focus, but we were last in a state where the top window is
            // hiding it.  This is probably because the keyguard as been
            // shown while the top window was displayed, so we want to ignore
            // it here because this is just a very transient change and it
            // will quickly lose focus once it correctly gets hidden.
            return 0;
        }
        //4 获取win的标志,处理rest flags
        int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null)
                & ~mResettingSystemUiFlags
                & ~mForceClearedSystemUiFlags;
        if (mForcingShowNavBar && win.getSurfaceLayer() < mForcingShowNavBarLayer) {
            tmpVisibility &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS);
        }
        //5 根据当前状态处理SYSTEM_UI_FLAG_LIGHT_STATUS_BAR标志(也就是status bar高亮状态)
        final int fullscreenVisibility = updateLightStatusBarLw(0 /* vis */,
                mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
        //6 导航栏高亮状态处理
        final int dockedVisibility = updateLightStatusBarLw(0 /* vis */,
                mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
        //7 获取home stack和dock stack的大小
        mWindowManagerFuncs.getStackBounds(HOME_STACK_ID, mNonDockedStackBounds);
        mWindowManagerFuncs.getStackBounds(DOCKED_STACK_ID, mDockedStackBounds);
        //8 更新status bar 的属性
        final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
        final int diff = visibility ^ mLastSystemUiFlags;
        final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags;
        final int dockedDiff = dockedVisibility ^ mLastDockedStackSysUiFlags;
        //9 更新各属性
        final boolean needsMenu = win.getNeedsMenuLw(mTopFullscreenOpaqueWindowState);
        if (diff == 0 && fullscreenDiff == 0 && dockedDiff == 0 && mLastFocusNeedsMenu == needsMenu
                && mFocusedApp == win.getAppToken()
                && mLastNonDockedStackBounds.equals(mNonDockedStackBounds)
                && mLastDockedStackBounds.equals(mDockedStackBounds)) {
            return 0;
        }
        mLastSystemUiFlags = visibility;
        mLastFullscreenStackSysUiFlags = fullscreenVisibility;
        mLastDockedStackSysUiFlags = dockedVisibility;
        mLastFocusNeedsMenu = needsMenu;
        mFocusedApp = win.getAppToken();
        final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds);
        final Rect dockedStackBounds = new Rect(mDockedStackBounds);
        //10请求StatusBarManager处理(最终请求status bar 处理)
        mHandler.post(new Runnable() {
                @Override
                public void run() {
                    StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
                    if (statusbar != null) {
                        statusbar.setSystemUiVisibility(visibility, fullscreenVisibility,
                                dockedVisibility, 0xffffffff, fullscreenStackBounds,
                                dockedStackBounds, win.toString());
                        statusbar.topAppWindowChanged(needsMenu);
                    }
                }
            });
        return diff;
    }

我们这里对以上10个步骤进行解释
1获取候选的window,也就是接收事件的window,这里可能有集中情况
(1)status bar 被展开或者keygurd的情况statusbar window是焦点的window
(2) 在分屏状态下,接收事件的那个window
(3) 在没有分屏状态下全屏的activity就是焦点
对于这里选择的候选window,默认以焦点为主,没有焦点时则使用mTopFullscreenOpaqueWindowState作为候选window,候选window用于使用它的flags处理系统bar的状态。 如果没有找到候选window 就直接返回。这里解释下mTopFullscreenOpaqueWindowState代表在full workspace stack上的activity
2 这里处理的情况是当前的焦点window 是如下这个window
这里写图片描述
当处于身临其境的模式,也就是设置了View.SYSTEM_UI_FLAG_IMMERSIVE |View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY|View.SYSTEM_UI_FLAG_HIDE_NAVIGATION标志的时候,防止用户恐慌,提示如何划出状态栏导航栏,这种状态不需要处理状态栏属性,直接等到用户退出帮助页面后再去操作
3 当有一个window设置了WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED标志的时候这个界面会在锁屏上面显示,这时候mHideLockScreen为真,注意这个标志在8.0里面改了这变量名,其实是一样的。这种情况马上焦点window就不是keygurd了,这只是焦点窗口切换的中间状态,这种情况直接不处理,等状态变了后再去处理
4 PolicyControl.getSystemUiVisibility(win, null)函数我们暂时怎为是直接获取到win的systemUiVisibility变量所描述的关于该window设置的systemui的一些flags。
另外对于mResettingSystemUiFlags的解释,当设置了View.SYSTEM_UI_FLAG_HIDE_NAVIGATION|View.SYSTEM_UI_FLAG_FULLSCREEN这连个标志而没有设置IMMERSIVE相关标志的时候,会隐藏掉状态栏和导航栏,这时候要求点击屏幕任意地方,状态栏和导航栏都出出来,这个功能的实现是创建一个全屏的事件消费者去接收屏幕点击事件,当用户点击后这个事件消费者会处理事件,事件消费者注册的地方是在PhoneWindowManager的beginLayoutLw()函数中,事件的处理是在PhoneWindowManager的内部类HideNavInputEventReceiver的onInputEvent()方法中,读者不妨自己去读一下,主要是通过给mResettingSystemUiFlags设置View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_FULLSCREEN 这三个标志,使status bar 出现来实现的。这个过程我们最后用一个情景栈去分析.
5 第五步是最终要的一步,前面的步骤都,用于更新systemui的一些标志,由于函数比较长,我们稍后分析.
6 在一些场景下要对高亮标志进行修正,我们这里只拿statusbar 作为例子说明

  private int updateLightStatusBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming) {
        WindowState statusColorWin = isStatusBarKeyguard() && !mHideLockScreen
                ? mStatusBar
                : opaqueOrDimming;

        if (statusColorWin != null) {
            if (statusColorWin == opaque) {
                // If the top fullscreen-or-dimming window is also the top fullscreen, respect
                // its light flag.
                vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
                vis |= PolicyControl.getSystemUiVisibility(statusColorWin, null)
                        & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
            } else if (statusColorWin != null && statusColorWin.isDimming()) {
                // Otherwise if it's dimming, clear the light flag.
                vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
            }
        }
        return vis;
    }

代码很简单,首先选择statusColorWin,规则是如果是keygurd且不被其他window覆盖则选择statusbar 作为statusColorWin,否则就是使用参数opaqueOrDimming(放在full stack中的), 当statusColorWin为opaque的window的时候(也就是full stack中不透明的window),这时候状态栏的高亮状态由opaque window决定. 如果不是full stack中的opaque window 则由window自身的Dimming状态决定
7 获取Home stack 和docker stack的大小保存在mNonDockedStackBounds,mDockedStackBounds变量中
8,9 更新我们计算好的systemui相关的属性
10请求StatusBarManager处理(最终请求Systemui 处理)

这里第五条和第十条我们分别展开说明
首先第五步骤更新systemuiFlags的过程,这个过程主要参考焦点window,mTopFullscreenOpaqueWindowState和mTopDockedOpaqueWindowState去计算bar的属性

private int updateSystemBarsLw(WindowState win, int oldVis, int vis) {
        final boolean dockedStackVisible = mWindowManagerInternal.isStackVisible(DOCKED_STACK_ID);
        final boolean freeformStackVisible =
                mWindowManagerInternal.isStackVisible(FREEFORM_WORKSPACE_STACK_ID);
        final boolean resizing = mWindowManagerInternal.isDockedDividerResizing();

        // We need to force system bars when the docked stack is visible, when the freeform stack
        // is visible but also when we are resizing for the transitions when docked stack
        // visibility changes.
        //1 判断是否强制显示system bar
        mForceShowSystemBars = dockedStackVisible || freeformStackVisible || resizing;
        final boolean forceOpaqueStatusBar = mForceShowSystemBars && !mForceStatusBarFromKeyguard;

        //2 计算fullscreenTransWin用于计算bar 的透明情况
        // apply translucent bar vis flags
        WindowState fullscreenTransWin = isStatusBarKeyguard() && !mHideLockScreen
                ? mStatusBar
                : mTopFullscreenOpaqueWindowState;
        //3 计算状态栏透明flags
        vis = mStatusBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
        //4 计算导航栏透明情况
        vis = mNavigationBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
        //5 计算docker导航栏透明度
        final int dockedVis = mStatusBarController.applyTranslucentFlagLw(
                mTopDockedOpaqueWindowState, 0, 0);
    //6 根据mTopFullscreenOpaqueWindowState计算fullscreen stack的FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS标志
        final boolean fullscreenDrawsStatusBarBackground =
                (drawsSystemBarBackground(mTopFullscreenOpaqueWindowState)
                        && (vis & View.STATUS_BAR_TRANSLUCENT) == 0)
                || forcesDrawStatusBarBackground(mTopFullscreenOpaqueWindowState);
       //7 根据dockedDrawsStatusBarBackground计算docker stack的FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS标志
        final boolean dockedDrawsStatusBarBackground =
                (drawsSystemBarBackground(mTopDockedOpaqueWindowState)
                        && (dockedVis & View.STATUS_BAR_TRANSLUCENT) == 0)
                || forcesDrawStatusBarBackground(mTopDockedOpaqueWindowState);

        // prevent status bar interaction from clearing certain flags
        int type = win.getAttrs().type;
        boolean statusBarHasFocus = type == TYPE_STATUS_BAR;
        //8 根据status 是否展开清除一些属性
        if (statusBarHasFocus && !isStatusBarKeyguard()) {
            int flags = View.SYSTEM_UI_FLAG_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_IMMERSIVE
                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                    | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
            if (mHideLockScreen) {
                flags |= View.STATUS_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSLUCENT;
            }
            vis = (vis & ~flags) | (oldVis & flags);
        }
        //9 根据FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS设置一些属性
        if (fullscreenDrawsStatusBarBackground && dockedDrawsStatusBarBackground) {
            vis |= View.STATUS_BAR_TRANSPARENT;
            vis &= ~View.STATUS_BAR_TRANSLUCENT;
        } else if ((!areTranslucentBarsAllowed() && fullscreenTransWin != mStatusBar)
                || forceOpaqueStatu
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TangGeeA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值