欢迎来到DIVCSS5查找CSS资料与学习DIV CSS布局技术!
Android SystemBar在Android中一直扮演着非常重要的角色,Google也一直在优化这一部分的用户体验,结合最近的工作体验碎碎念 ROM 去掉Navigationbar、SystemBar Translucent支持、SystemBar setColor支持几个小点。
 
ROM 去掉NavigationBar
 
resgrep NavigationBar相关的res,发现在framework/base/core/res/res/config.xml中可以通过config_showNavigationBar配置,继续jgrep showNavigationBar相关的java,发现在PhoneWindowManager中通过boolean变量mHasNavigationBar记录是否有NavigationBar,具体代码如下:
 
mHasNavigationBar = res.getBoolean(com.android.internal.R.bool.config_showNavigationBar);
 
需要注意的是mHasNavigationBar也可以通过系统提供的Property来标识,具体代码如下:
 
String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");
 
if ("1".equals(navBarOverride)) {
 
    mHasNavigationBar = false;
 
} else if ("0".equals(navBarOverride)) {
 
    mHasNavigationBar = true;
 
}
 
在PhoneWindowManager中存在一个类型为WindowState的变量mNavigationBar,WindowState是在PhoneWindowManger prepareAddWindow时会用到的一个interface,仔细看看prepareAddWindowLw方法
 
switch (attrs.type) {
 
    case TYPE_NAVIGATION_BAR:
 
       mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE, "PhoneWindowManager");
 
        if (mNavigationBar != null) {
 
            if (mNavigationBar.isAlive()) {
 
                return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
 
            }
 
        }
 
        mNavigationBar = win;
 
        mNavigationBarController.setWindow(win);
 
        if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar);
 
       break;
 
}
 
判断type为TYPE_NAVIGATION_BAR的时候,给mNavigationBar赋值,并且mNavigationBarController setWindow,mNavigationBarController是一个用来操纵SystemBar Window的类,有兴趣可以详细看看这个类,位于/framework/base/services/core/java/com/android/server/policy。继续浏览代码会发现方法layoutNavigationBar,通过mNavigationBarController对mNavigationBar进行layout。
 
在framework/base/packages/SystemUI中PhoneStatusBar中makeStatusBarView有如下一段代码:
 
try {
 
    boolean showNav = mWindowManagerService.hasNavigationBar();
 
    if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
 
    if (showNav) {
 
        createNavigationBarView(context);
 
    }
 
} catch (RemoteException ex) {
 
    // no window manager? good luck with that
 
}
 
根据WindowManagerService中的hasNavigationBar方法判断是否有NavigationBar,再看WindowMangerService中是调用了PhoneWindowManager中的hasNavigationBar方法,最终返回的就是最开始提到了mHasNavigationBar这个标识。在PhoneStatusBar有详细讲到如何createAndAddWindows,感兴趣可以再进一步了解。
 
SystemBar Translucent支持
 
Android在API 19的时候添加了两个WindowManager.LayoutParams,分别是FLAG_TRANSLUCENT_NAVIGATION、FLAG_TRANSLUCENT_STATUS。
 
在官方文档中有提到当为Window设置FLAG_TRANSLUCENT_NAVIGATION时,自动设置了System UI visibility SYSTEM_UI_FLAG_LAYOUT_STABLE、SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION,当为Window设置了FLAG_TRANSLUCENT_STATUS时,自动设置了System UI visibility SYSTEM_UI_FLAG_LAYOUT_STABLE、SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN。
 
显然,这时System UI visibility有发生变化,在PhoneWindowManager中有如下一段代码:
 
public int adjustSystemUiVisibilityLw(int visibility) {
 
        mStatusBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
 
        mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
 
        mRecentsVisible = (visibility & View.RECENT_APPS_VISIBLE) > 0;
 
        mTvPictureInPictureVisible = (visibility & View.TV_PICTURE_IN_PICTURE_VISIBLE) > 0;
 
        // Reset any bits in mForceClearingStatusBarVisibility that
 
        // are now clear.
 
        mResettingSystemUiFlags &= visibility;
 
        // Clear any bits in the new visibility that are currently being
 
        // force cleared, before reporting it.
 
        return visibility & ——mResettingSystemUiFlags
 
            & ——mForceClearedSystemUiFlags;
 
}
 
可以看到前边提到的操控SystemBar的Controller这个时候发挥了作用,调用了其adjustSystemUiVisibilityLw,继续看BarController中相关代码
 
public void adjustSystemUiVisibilityLw(int oldVis, int vis) {
 
    if (mWin != null && mTransientBarState == TRANSIENT_BAR_SHOWING && (vis & mTransientFlag) == 0) {
 
        // sysui requests hide
 
        setTransientBarState(TRANSIENT_BAR_HIDING);
 
        setBarShowingLw(false);
 
    } else if (mWin != null && (oldVis & mUnhideFlag) != 0 && (vis & mUnhideFla == 0) {
 
        // sysui ready to unhide
 
        setBarShowingLw(true);
 
    }
 
}
 
同时,看到BarController中有applyTranslucentFlagLw,通过查找其调用处,发现依旧是在PhoneWindowManager中调用,以下是调用部分
 
// apply translucent bar vis flags
 
WindowState fullscreenTransWin = isStatusBarKeyguard() && !mHideLockScreen ? mStatusBar : mTopFullscreenOpaqueWindowState;
 
vis = mStatusBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVs);
 
vis = mNavigationBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
 
final int dockedVis = mStatusBarController.applyTranslucentFlagLw(mTopDockedOpaqueWindowState, 0, 0);
 
final boolean fullscreenDrawsStatusBarBackground =
 
        (drawsSystemBarBackground(mTopFullscreenOpaqueWindowState) && (vis & View.STATUS_BAR_TRANSLUCENT) == 0) || forcesDrawStatusBarBackground(mTopFullscreenOpaqueWindowState);
 
final boolean dockedDrawsStatusBarBackground = (drawsSystemBarBackground(mTopDockedOpaqueWindowState) && (dockedVis & View.STATUS_BAR_TRANSLUCENT) == 0) || forcesDrawStatusBarBackground(mTopDockedOpaqueWindowState);
 
可以看到在PhoneWindowManager中通过updateSystemBarsLw进行了相应的更新SystemBars。这个地方还有一个比较有意思的问题是,在实际使用这两个Flag的时候,会发现App自动延伸到StatusBar和NavigationBar下方,这是为何?
 
猜想,应该是App区域对系统下发的WindowInsets值做了处理,查看ActionBarOverlayLayout onApplyWindowInsets方法,可以发现通过computeFitSystemWindows对ActionBarOverlayLayout的measure时用到的insets值做了处理,查看View的computeFitSystemWindows方法
 
protected boolean computeFitSystemWindows(Rect inoutInsets, Rect outLocalInsets) {
 
    if ((mViewFlags & OPTIONAL_FITS_SYSTEM_WINDOWS) == 0 || mAttachInfo == null || ((mAttachInfo.mSystemUiVisibility & SYSTEM_UI_LAYOUT_FLAGS) == 0 && !mAttachInfo.mOverscanRequested)) {
 
        outLocalInsets.set(inoutInsets);
 
        inoutInsets.set(0, 0, 0, 0);
 
        return true;
 
    } else {
 
        // The application wants to take care of fitting system window for
 
        // the content...  however we still need to take care of any overscan here.
 
        final Rect overscan = mAttachInfo.mOverscanInsets;
 
        outLocalInsets.set(overscan);
 
        inoutInsets.left -= overscan.left;
 
        inoutInsets.top -= overscan.top;
 
        inoutInsets.right -= overscan.right;
 
        inoutInsets.bottom -= overscan.bottom;
 
        return false;
 
    }
 
}
 
可以发现这个地方,根据flag的判断通过overscan Rect对ActionBarOverlayLayout的insets值进行了修改(最近把这个处理移植到API 19以下了,嘿嘿嘿)。当然Google也没勇气对这个处理一锅端,因此App可以使用fitsSystemWindows属性来避免系统做这个处理,通过这个属性可以默认把insets值给到当前View的padding值,当然App也可以自己继承View的fitSystemWindows方法来自己处理insets。
 
SystemBar setColor支持
 
Android在API 21的时候为Window添加了setNavigationBarColor、setStatusBarColor,进一步提升SystemBar用户体验。
 
PhoneWindow继承Window具体实现了setNavigationBarColor、setStatusBarColor,具体代码如下:
 
public void setStatusBarColor(int color) {
 
    mStatusBarColor = color;
 
    mForcedStatusBarColor = true;
 
    if (mDecor != null) {
 
        mDecor.updateColorViews(null, false /* animate */);
 
    }
 
}
 
public void setNavigationBarColor(int color) {
 
    mNavigationBarColor = color;
 
    mForcedNavigationBarColor = true;
 
    if (mDecor != null) {
 
        mDecor.updateColorViews(null, false /* animate */);
 
        mDecor.updateNavigationGuardColor();
 
    }
 
}
 
不难发现主要是DecorView的updateColorViews在work,通过查看代码,可以明白是DecorView在SystemBar的位置add了对应的ColorStateView,这个有点类似PhoneWindowManager里边的WindowState,之后对ColotStateView里边的view进行操作即可,比如说setBackground来改变其颜色。

如需转载,请注明文章出处和来源网址:http://www.divcss5.com/html/h63261.shtml