概述

android 自4.4之后,开始支持半透明状态栏效果。
一般都是将布局延伸到状态栏下,并且保持和toolbar颜色一致,就是我们所说的变色状态栏。

比如我们经常用到的网易云音乐的状态栏处理 和 miui内置应用的状态栏处理都采用了这种方式,如下:
android变色状态栏_第1张图片

实现

参考:Android 沉浸式状态栏攻略 让你的状态栏变色吧

关于变色状态栏的实现也非常的多,主要都是通过 4.4 之上的theme和fitsSystemWindows属性来实现的。

fitsSystemWindows

通过调整view的padding来自动适应屏幕,具体表现:
设置为true时

  • api>19时,view的paddingTop=status bar 的高度保障content正常显示。
  • api<19时,paddingTop =0 ,因为content并没有沿伸到status bar中。

style

  • values-v19/styles.xml
<resources>    <style name="AppTheme" parent="@style/BaseAppTheme"> <item name="android:windowTranslucentStatus">true</item> </style></resources>

按照上面的设置,状态栏透明且显示的是activity的background,如下如所示。这时候就需要在状态栏的位置设置一个颜色和 toolbar相同的view 即可。这里可以使用开源库SystemBarTint

android变色状态栏_第2张图片

SystemBarTint:

 SystemBarTintManager tintManager=new SystemBarTintManager(this);        tintManager.setStatusBarTintResource(R.color.colorPrimaryDark);        tintManager.setStatusBarTintEnabled(true);

最终效果如下图:
android变色状态栏_第3张图片

这里navigationView并没有沿伸到状态栏下,而是被状态栏盖住,和网易云音乐处理的还是优点差距,那么看一下SystemBarTint源码吧。

SystemBarTint源码解析

扒开SystemBarTint的源码,我们可以看到作者的设计思路也是这样的,

首先,通过反射获取是否存在navigation bar,

static {        // Android allows a system property to override the presence of the navigation bar.        // Used by the emulator.        // See https://github.com/android/platform_frameworks_base/blob/master/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java#L1076        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {            try {                Class c = Class.forName("android.os.SystemProperties");                Method m = c.getDeclaredMethod("get", String.class);                m.setAccessible(true);                sNavBarOverride = (String) m.invoke(null, "qemu.hw.mainkeys");            } catch (Throwable e) {                sNavBarOverride = null;            }        }    }

这里有一个SystemBarConfig类,主要是记录设备的屏幕参数的,主要有statusbar的信息和navigationsbar的信息和是否包含navigationsbar等等。

然后,在构造函数中,通过获取activity的style来获取是否设置了Translucent主题

// check theme attrs            int[] attrs = {android.R.attr.windowTranslucentStatus,                    android.R.attr.windowTranslucentNavigation};            TypedArray a = activity.obtainStyledAttributes(attrs);            try {                mStatusBarAvailable = a.getBoolean(0, false);                mNavBarAvailable = a.getBoolean(1, false);            } finally {                a.recycle                }

最终调用,通过设置好的Color 或者Drawable 来 添加(mStatusBarTintView)

private void setupStatusBarView(Context context, ViewGroup decorViewGroup) {        mStatusBarTintView = new View(context);        LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, mConfig.getStatusBarHeight());        params.gravity = Gravity.TOP;        if (mNavBarAvailable && !mConfig.isNavigationAtBottom()) {            params.rightMargin = mConfig.getNavigationBarWidth();        }        mStatusBarTintView.setLayoutParams(params);        mStatusBarTintView.setBackgroundColor(DEFAULT_TINT_COLOR);        mStatusBarTintView.setVisibility(View.GONE);        decorViewGroup.addView(mStatusBarTintView);    }    private void setupNavBarView(Context context, ViewGroup decorViewGroup) {        mNavBarTintView = new View(context);        LayoutParams params;        if (mConfig.isNavigationAtBottom()) {            params = new LayoutParams(LayoutParams.MATCH_PARENT, mConfig.getNavigationBarHeight());            params.gravity = Gravity.BOTTOM;        } else {            params = new LayoutParams(mConfig.getNavigationBarWidth(), LayoutParams.MATCH_PARENT);            params.gravity = Gravity.RIGHT;        }        mNavBarTintView.setLayoutParams(params);        mNavBarTintView.setBackgroundColor(DEFAULT_TINT_COLOR);        mNavBarTintView.setVisibility(View.GONE);        decorViewGroup.addView(mNavBarTintView);    }

经过分析,这里是因为这行代码

// 在跟布局添加mStatusBarTintView   decorViewGroup.addView(mStatusBarTintView);

只需要修改上述代码如下即可

private void setupStatusBarView(Context context, ViewGroup decorViewGroup) {        ViewGroup root = (ViewGroup) decorViewGroup.findViewById(android.R.id.content);        mStatusBarTintView = new View(context);        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, mConfig.getStatusBarHeight());        params.gravity = Gravity.TOP;        if (mNavBarAvailable && !mConfig.isNavigationAtBottom()) {            params.rightMargin = mConfig.getNavigationBarWidth();        }        mStatusBarTintView.setLayoutParams(params);        mStatusBarTintView.setBackgroundColor(DEFAULT_TINT_COLOR);        mStatusBarTintView.setVisibility(View.GONE);        //在content中添加mStatusBarTintView        root.addView(mStatusBarTintView, 0);    }

这里涉及到一个问题:addView(child) 和addView(child,0)的区别:

前者调用的是addView(child,-1),传入的 -1代表直接添加到 view的最后
传入0代表 添加到第一个

封装

设置状态栏颜色已经很普遍了,那考虑全面一点

  • 可设置透明度
  • 可设置是否覆盖
  • 考虑是否包含navigationsbar
  • 考虑5.0以上情况
StatusBarUtils.instance(this).setColor(color).                setStyle(isFill ? StatusBarUtils.TYPE.FILL : StatusBarUtils.TYPE.NOMAL).init();

来一张效果图:

源码:ColorStatusBar

关于android 变色状态栏相关开源库
StatusBarCompat
StatusBarAdapt

更多相关文章

  1. 从Android 6.0源码的角度剖析View的事件分发机制
  2. Android消息机制源码解析(一)——消息的载体Message
  3. Android异步消息机制Handler详解,源码剖析(API 23)
  4. Android事件总线(四)源码解析otto
  5. Android中Canvas绘图基础详解(附源码下载)
  6. android 系统的开机启动流程源码解析(从linux 内核到android Laun
  7. Android主流三方库源码分析(二、深入理解Retrofit源码)

随机推荐

  1. Android中的常用控件
  2. Android Bluetooth源码分析总结 - framew
  3. Android软键盘弹出,界面整体上移
  4. Android开发手记--环境配置
  5. Android(安卓)bitmap config你理解对了吗
  6. Android进程间通信 — AIDL
  7. Android 生成keystore,两种方式 【包括Mac
  8. imageView的使用(进行原样的保持和按照比
  9. Android实现截屏方式
  10. Android:Animation 使用手册