浅析Android 9.0导航栏的变化

资源变化

在Android 8.0及以前,导航栏NavigationBarView上的每个KeyButton的图标资源几乎都是用的PNG图片,这类资源使用通常需要在不同分辨率的drawable-xxx目录下面都加入相应的文件,这就导致一些客制化需求的修改也变得比较费事。

矢量图在不同分辨率下经过放大或缩小后,图片的质量都不会下降,使用矢量图替代PNG资源可以降低应用内存的占用。在9.0之前的版本,Google已经在逐步使用Vector矢量图来替换PNG资源,比如导航栏上最右边的切换输入法的按钮,在8.0版本上已经改为了矢量图,而到了9.0版本上导航栏的所有按钮资源都已经被替换成矢量图。

<!-- back按钮的Vector资源 -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="28dp"
    android:height="28dp"
    android:viewportWidth="28"
    android:viewportHeight="28">

    <path
        android:fillColor="?attr/singleToneColor"
        android:pathData="M6.49,14.86c-0.66-0.39-0.66-1.34,0-1.73l6.02-3.53l5.89-3.46C19.11,5.73,20,6.26,20,7.1V14v6.9 c0,0.84-0.89,1.37-1.6,0.95l-5.89-3.46L6.49,14.86z" />
</vector>


黑白色按钮

其实这一点的变化并不大,之前已经提到在8.0版本上已经有图标开始使用矢量图了,所以后续9.0上也是用同样的做法修改的。

我们知道Android 8.0上谷歌引入了一个新的flag:View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,应用可以通过使用这个flag来轻松切换导航栏上的按钮的颜色(黑 - 白):SystemUI上负责显示按钮的是一类KeyButtonDrawable,这个类继承自LayerDrawable,它会把drawable资源层叠显示,我们设置的白色与黑色的drawable会被一个在上一个在下的放在这个容器中,当需要切换颜色时,KeyButtonDrawable会通过分别设置两边的alpha透明度的方式来实现。

8.08.1版本上,NavigationBarView需要传入两个drawable资源,一个黑色的一个白色的:

    private void updateIcons(Context ctx, Configuration oldConfig, Configuration newConfig) {
        ...
        if (oldConfig.densityDpi != newConfig.densityDpi
                || oldConfig.getLayoutDirection() != newConfig.getLayoutDirection()) {
            mBackIcon = getDrawable(ctx, R.drawable.ic_sysbar_back, R.drawable.ic_sysbar_back_dark);
            ...
            mHomeDefaultIcon = getDrawable(ctx,
                    R.drawable.ic_sysbar_home, R.drawable.ic_sysbar_home_dark);
            mRecentIcon = getDrawable(ctx,
                    R.drawable.ic_sysbar_recent, R.drawable.ic_sysbar_recent_dark);
            ...
        }
    }

而在9.0上由于这些Vector的fillColor用的是attr值,之后可根据不同attr下获得相应的Context来设置不同的color,所以只需要传入一个xml的drawable资源即可:

    private void updateIcons(Context ctx, Configuration oldConfig, Configuration newConfig) {
        int dualToneDarkTheme = Utils.getThemeAttr(ctx, R.attr.darkIconTheme);
        int dualToneLightTheme = Utils.getThemeAttr(ctx, R.attr.lightIconTheme);
        Context lightContext = new ContextThemeWrapper(ctx, dualToneLightTheme);
        Context darkContext = new ContextThemeWrapper(ctx, dualToneDarkTheme);
        ...
        if (oldConfig.densityDpi != newConfig.densityDpi
                || oldConfig.getLayoutDirection() != newConfig.getLayoutDirection()) {
            mBackIcon = getBackDrawable(lightContext, darkContext);
            mRecentIcon = getDrawable(lightContext, darkContext, R.drawable.ic_sysbar_recent);
            ...
        }
    }

    private KeyButtonDrawable getDrawable(Context lightContext, Context darkContext,
            @DrawableRes int icon, boolean hasShadow) {
        return KeyButtonDrawable.create(lightContext, lightContext.getDrawable(icon),
                darkContext.getDrawable(icon), hasShadow);
    }

并且在使用了矢量图后,9.0也不再需要保留箭头朝下的back按钮图标资源(软键盘弹出后back键会变成箭头朝下),KeyButtonDrawable内部已经实现了用于图标旋转的方法:定义rotateDegrees值后调用invalidateSelf(),然后在draw()方法里根据rotateDegrees来旋转Canvas。

屏幕旋转

在绝大部分时候 —— 尤其是躺在床上的时候,为了避免屏幕突然被旋转,我们可能都会将屏幕自动旋转功能关闭。毕竟,我们并非时时都需要横屏观看内容。

但一旦需要让屏幕旋转时,我们又得手动在快捷设置中打开这个开关,长此以往,实在有些麻烦。

面对这种情况,Android 9.0 提供了一个更有「灵性」的解决方案:在屏幕旋转方向锁定的情况下,系统依然会检测你的设备方向;这时当你将手机横过来之后,屏幕内导航栏区域就会出现一个「自动旋转」开关,点击就能打开并旋转屏幕内容:


自动旋转按钮与其他常见按钮不太一样,它是AnimatedVectorDrawable,这是一个动态的矢量图,其xml资源文件需要使用animated-vector标签,并在里面定义一系列动画效果,就是上图gif里演示的小手机旋转的动画。关于AnimatedVectorDrawable,有兴趣的同学可以自行搜索了解一下。

    public void setRotateSuggestionButtonState(final boolean visible, final boolean force) {
        if (mNavigationBarView == null) return;

        // At any point the the button can become invisible because an a11y service became active.
        // Similarly, a call to make the button visible may be rejected because an a11y service is
        // active. Must account for this.

        ButtonDispatcher rotBtn = mNavigationBarView.getRotateSuggestionButton();
        final boolean currentlyVisible = mNavigationBarView.isRotateButtonVisible();

        // Rerun a show animation to indicate change but don't rerun a hide animation
        if (!visible && !currentlyVisible) return;

        View view = rotBtn.getCurrentView();
        if (view == null) return;

        KeyButtonDrawable kbd = rotBtn.getImageDrawable();
        if (kbd == null) return;

        // The KBD and AVD is recreated every new valid suggestion because of style changes.
        AnimatedVectorDrawable animIcon = null;
        if (kbd.getDrawable(0) instanceof AnimatedVectorDrawable) {
            animIcon = (AnimatedVectorDrawable) kbd.getDrawable(0);
        }

        // Handle the visibility change and animation
        if (visible) { // Appear and change (cannot force)
            // Stop and clear any currently running hide animations
            if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) {
                mRotateHideAnimator.cancel();
            }
            mRotateHideAnimator = null;

            // Reset the alpha if any has changed due to hide animation
            view.setAlpha(1f);

            // Run the rotate icon's animation if it has one
            if (animIcon != null) {
                animIcon.reset();
                animIcon.start();
            }
            ...
        }

    }


布局变化

Android 9.0 导航栏上最Cool的变化,自然是新的导航手势及“药丸”按钮。

导航手势

由于新增了一种导航手势,自然NavigationBarInflaterView加载的布局也多了一种。
8.0的加载默认布局:

    protected String getDefaultLayout() {
        return mContext.getString(R.string.config_navBarLayout);
    }

9.0的加载默认布局:

    protected String getDefaultLayout() {
        final int defaultResource = mOverviewProxyService.shouldShowSwipeUpUI()
                ? R.string.config_navBarLayoutQuickstep
                : R.string.config_navBarLayout;
        return mContext.getString(defaultResource);
    }

当使用了Quickstep的布局时,导航栏就会变成这种效果:




关于9.0上的手势的使用方法,可移步至以下文章:
https://baijiahao.baidu.com/s?id=1600087701707774194&wfr=spider&for=pc

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容