一:概述

Android4.4以及Android5.0之后,Android上很多APP都出现了如下效果:


简单的说就是状态栏颜色出现了变化,能够跟APP界面颜色相匹配。接下来对这种效果的实现进行详细的分析。

二:Android窗口介绍

先来了解一下Android的窗口系统。从用户角度来看,Android是一个多窗口的操作系统,窗口根据尺寸,位置,Z-Order,透明度等参数叠加起来一起呈现给用户。这些窗口既可以来自同一个应用也可以来自不同的应用。既可以显示在同一个平面,也可以在不同的平面。总而言之,窗口是有层次的显示区域,每个窗口在底层最终体现为一个个的矩形buffer,这些buffer通过计算合成一个新的buffer,最终交给Display系统进行显示。为了辅助最后的窗口管理,Android定义了一些不同的窗口类型:

  • 应用程序窗口(Application Window):包括应用程序自己创建的窗口,以及应用启动之前系统负责显示的窗口。
  • 子窗口(Sub Window):应用自定义的对话框,输入法窗口,子窗口必须依附于某个应用窗口(设置相同的token)。
  • 系统窗口(System Window):系统设计的,不依附于任何应用的窗口,列如状态栏(Status Bar),导航栏(Navigation Bar),壁纸(Wallpaper),来电显示窗口(Phone),锁屏窗口(KeyGuard),信息提示窗口(Toast),音量调试窗口等。

PhoneWindow是Activity Window的扩展,是为手机或者平板专门设计的一个布局方案,一个PhoneWindow的布局大致如下:



三:Android4.4上的SystemBar

Android4.4以下对于系统窗口中的状态栏和导航栏等是不支持更改的。但是Android4.4API里google添加了一个这translucent statusbar navigationbar概念。官方文档如下:

Translucent system bars

You can now make the system bars partially translucent with new themes, Theme.Holo.NoActionBar.TranslucentDecor and Theme.Holo.Light.NoActionBar.TranslucentDecor. By enabling translucent system bars, your layout will fill the area behind the system bars, so you must also enable fitsSystemWindows for the portion of your layout that should not be covered by the system bars.

If you’re creating a custom theme, set one of these themes as the parent theme or include the windowTranslucentNavigation and windowTranslucentStatus style properties in your theme.

android 4.4虽然没有直接提供修改系统栏(状态栏,导航栏)颜色,但是却给了我们实现系统栏设计的方向。

首先,设置透明系统栏。

方法一:设置Activity所在window的属性。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){         getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);         getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);     }

方法二:设置theme属性。

android:theme="@android:style/Theme.DeviceDefault.Light.NoActionBar.TranslucentDecor"android:theme="@android:style/Theme.Holo.Light.NoActionBar.TranslucentDecor"android:theme="@android:style/Theme.Holo.NoActionBar.TranslucentDecor"

如果使用自定主题,只需在在 values-19 文件夹下添加以下属性

<style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar" >    <!-- API 19 theme customizations can go here. -->    <item name="android:windowTranslucentStatus">true</item>    <item name="android:windowTranslucentNavigation">true</item></style>

但是,设置透明之后效果如下所示,app会顶掉原来状态栏所占位置,解决方法如下。


Android 透明式系统栏设计_第1张图片


设置fitsystemWindow

关于fitsystemwindow属性,官方文档如下:

Boolean internal attribute to adjust view layout based on system windows such as the status bar. If true, adjusts the padding of this view to leave space for the system windows. Will only take effect if this view is in a non-embedded activity.

Must be a boolean value, either “true” or “false”.

也就是说,设置fitsSystemWindow=true后,view会根据系统栏自动设置Padding值来适配。设置之后效果如下:



跟进去之后可以看到,里面最终调用这么view的这个函数:protected boolean fitSystemWindows (Rect insets)

  • 关于 window insets

如果你查看 View 的 protected boolean fitSystemWindows (Rect insets) 函数,其参数值为 Rect insets, 关于该参数的说明如下:

Rect: Current content insets of the window. Prior to JELLY_BEAN you must not modify the insets or else you and Android will be unhappy.

返回值的说明如下:

true if this view applied the insets and it should not continue propagating further down the hierarchy, false otherwise.

content insets 是系统状态栏、导航栏、输入法等其他系统窗口所占用的空间,系统通过该回调接口告诉你的应用, 系统的窗口占用了多少空间,然后你可以根据该信息来调整你的 View 显示属性。

  • 自定义 fitsSystemWindows

在 android4.4+ 版本中,你可以重写这个 protected boolean fitSystemWindows (Rect insets) 函数,如果一个 View 想吃掉这个 insets ,则返回 true,如果不想处理,让其他 View 来处理,则返回 false。

为了更方便的处理这种情况,在 android5.0 版本,fitSystemWindows() 函数给废弃了,使用两个新的函数来简化该处理流程。使用新的 onApplyWindowInsets() 函数,该函数中你可以选择吃掉一部分insets,然后你还可以调用 dispatchApplyWindowInsets() 函数让该 View 的子View 来继续处理 insets。

如果你只想在 5.0 以上版本使用该功能,则无需继承一个 View, 只需要使用 ViewCompat.setOnApplyWindowInsetsListener() 函数即可。使用 ViewCompat 提供的函数可以方便你编写版本兼容的代码,不需要频繁的判断版本号。

  • 系统实例

系统的基本控件((FrameLayout, LinearLayout, 等)都使用默认的行为,Support 包中有些控件使用了自定义行为。

DrawerLayout 使用 fitsSystemWindows 来表明需要处理 insets,但是仍然使用状态栏的颜色来绘制状态栏背景(状态栏颜色为 主题的 colorPrimaryDark 所设置的颜色)。

然后 DrawerLayout 在每个子 View 上调用 dispatchApplyWindowInsets() 函数,这样 子 View 也有 机会处理 insets,这和系统默认行为是不一样的(系统默认行为只是吃掉这个 insets,然后子 View 无法继续处理)。

CoordinatorLayout 对此也做了特殊处理,让每个子 View 的 Behavior 可以根据系统窗口的大小来做不同的处理。 还使用 fitsSystemWindows 属性来判断是否需要绘制状态栏背景。

通用 CollapsingToolbarLayout 也根据 fitsSystemWindows 属性来确定何时何地绘制 内容上方的半透明背景。

系统栏着色

通过上面的步骤,状态栏透明且不会与内容冲突,但是现在问题是状态栏的颜色。

  • 方法一

首先我们清楚Android 整个界面是一个Window(phonewindow),而window的内部类DecorView是界面的根布局(FrameLayout),根据这种ui布局方式的话,可以设置activity的根布局的背景颜色即可更换导航栏颜色,只是需要添加一个子布局遮挡内容区域的背景颜色,效果图如下:


Android 透明式系统栏设计_第2张图片 Android 透明式系统栏设计_第3张图片
  • 方法二

上面的方法虽然简单但并不是合理的方法,还有一种方法就是,自己绘制一个与状态栏等高的view设置上颜色,填充进去,由于底层是透明的framelayout,故效果与其他各方面都最好,算是比较好的解决这个问题的一种方法。效果如下:


Android 透明式系统栏设计_第4张图片

完整代码如下:

package com.swallow.systembardesign;import android.os.Build;import android.os.Bundle;import android.app.Activity;import android.graphics.Color;import android.view.Gravity;import android.view.Menu;import android.view.View;import android.view.ViewGroup;import android.view.WindowManager;import android.widget.FrameLayout.LayoutParams;public class MainActivity extends Activity {    @Override    protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {        getWindow().addFlags(            WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);        getWindow().addFlags(            WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);        addStatusView();    }    }    @Override    public boolean onCreateOptionsMenu(Menu menu) {    // Inflate the menu; this adds items to the action bar if it is present.    getMenuInflater().inflate(R.menu.main, menu);    return true;    }    /**     * 获取状态栏的高度     *      * @return     */    private int getStatusHeight() {    int height = -1;    try {        Class<?> clazz = Class.forName("com.android.internal.R$dimen");        Object object = clazz.newInstance();        height = Integer.parseInt(clazz.getField("status_bar_height")            .get(object).toString());    } catch (Exception e) {        e.printStackTrace();    }    int statusHeight = this.getResources().getDimensionPixelSize(height);    return statusHeight;    }    /**     * 添加导航栏颜色布局     */    private void addStatusView() {    View mStatusView = new View(this);    LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,        getStatusHeight());    params.gravity = Gravity.TOP;    mStatusView.setLayoutParams(params);    mStatusView.setBackgroundColor(Color.RED);    ((ViewGroup) (getWindow().getDecorView())).addView(mStatusView);    }}

基于这种原理实现状态栏着色的Github上面有一个开源库–SystemBarTint,写的挺全面的,可以直接拿下来用。

三:Android5.0以上的SystemBar

Android5.0之后,Google提供了Material Design系列的API直接设置系统栏颜色。

<resources>  <!-- inherit from the material theme -->  <style name="AppTheme" parent="android:Theme.Material">    <!-- Main theme colors -->    <!--   your app branding color for the app bar -->    <item name="android:colorPrimary">@color/primary</item>    <!--   darker variant for the status bar and contextual app bars -->    <item name="android:colorPrimaryDark">@color/primary_dark</item>    <!--   theme UI controls like checkboxes and text fields -->    <item name="android:colorAccent">@color/accent</item>  </style></resources>

材料主题可让您轻松定制状态栏,因此您可以指定一个符合自己品牌特色且对比度足够高、能够显示白色状态图标的颜色。 如果要为状态栏设置定制颜色,您可在扩展材料主题时使用 android:statusBarColor 属性。 默认情况下,android:statusBarColor 将继承 android:colorPrimaryDark 的值。

您也可自行将状态栏移到后侧。例如,您想在一个照片上以透明的方式显示状态栏,同时利用细微的深色渐变以确保白色状态图标仍保持可见。 如果要执行此操作,请将 android:statusBarColor 属性设置为 @android:color/transparent 并根据需要调整窗口标志。您也可以使用 Window.setStatusBarColor() 方法进行动画或淡出设置。


Android 透明式系统栏设计_第5张图片


需要注意的是Android5.0之后的support包里的组件大都重写了与fitssystemwindow相关的onApplyWindowInsets(),dispatchApplyWindowInsets()两个方法,或将padding填充方式改变或者由子布局来完成填充任务,甚至将fitssystemwindow的值作为是否绘制状态栏的标志。

更多相关文章

  1. 13、Android重写系统返回键
  2. Android 系统永不休眠
  3. Android中如何修改CheckBox的颜色
  4. Android 4.0系统源码目录结构详解
  5. Android应用程序调用系统解锁页面
  6. android 修改状态栏和标题栏颜色
  7. 改变ScrollView的滚动条的颜色
  8. android 调用系统应用
  9. android实现调用系统音乐播放器

随机推荐

  1. Perl登陆Android
  2. adb使用-详细教程(Awesome Adb)
  3. Android自动化测试框架Robotium
  4. Android多线程下载远程图片
  5. Android(安卓)Service,startService binde
  6. android之resources资源
  7. 如何在Eclipse中配置已经存在到Android(
  8. Android(安卓)单元测试(四) 之AndroidJUn
  9. Android(安卓)开发的常用工具类(一)——Scr
  10. Android(安卓)上下文菜单:给ListView注册C