以前写的项目基本没有考虑过Android透明状态栏的,最近项目有用到,便网上搜索一番。搜到的文章要么是讲解不全、存在错误,要么就是使用一些库,也不说原理,上来就是给你看效果图,然后十几个方法让你眼花缭乱,看源码就是越看越复杂。算了,自己写个简单能用的就好。所以,本文将通俗易懂地介绍Android透明状态栏原理,以及几种常见使用场景的处理方法。

下面是实现的效果:


以上效果支持状态栏和标题栏(ToolBar或者自定义)背景一致且可修改(改变颜色、渐变色、不透明度看你自己怎么改),标题栏位置不被状态栏遮挡,6.0之上浅色状态栏背景修改字体颜色为深色。支持DrawerLayout使用、Fragment使用。虽然效果很多,其实原理都是一样的。

本文会教你如何修改状态栏的颜色、如何解决状态栏和标题栏内容重合的问题。

顺便说下,这里的透明状态栏指的是布局内容延伸到状态栏的效果,网上很多人也叫做沉浸式状态栏,这里不做辩驳。

原理

透明状态栏的设置存在兼容性。Android 4.4(API 19)之前不支持透明状态栏,而Android 5.0(API 21)之后版本对于透明状态栏则有不同的处理方式。所以,我们要兼容的就是保证4.4之前的版本虽然不支持透明状态栏,但是UI效果还是要协调,而4.4之后的版本跟根据不同的API版本尽量达成一致的显示效果。

设置透明状态栏,最核心的方法就是下面这一个方法:

 public static void makeStatusBarTransparent(Activity activity) {        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {            return;        }        Window window = activity.getWindow();        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);            int option = window.getDecorView().getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;            window.getDecorView().setSystemUiVisibility(option);            window.setStatusBarColor(Color.TRANSPARENT);        } else {            window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);        }    }

你应该在网上找到了很多版本,类似的代码。归根到底,就这一个方法对Activity进行设置即可实现透明状态栏。这个方法调用后,页面布局内容就会延伸至状态栏,状态栏即为透明色。


但是,这里要注意下,其带来了一个问题是页面布局会延伸至状态栏,你布局中的标题栏和状态栏重合了,这显示不是我们想要的效果。我们想要的是原有标题栏还是在原来标题栏的位置,只不过状态栏的颜色和标题栏一致。接下来,我们将要解决的就是标题栏和状态栏重合的问题。这个问题,也是你看到的所有第三方库想要帮你解决的问题。第三方库为了满足广大开发者的需求,也是为了适配各种情况,通常会增加N多种接口或者方式来处理这个问题,但随之而来带来的就是复杂性,不易理解。接口方法太多了,通常都要组合搭配调用才能生效,不看源码,光看接口估计会很懵逼。

好勒,接下来,我们就介绍几种方式来解决标题栏和状态栏重合问题。

标题栏和状态栏重合问题

解决状态栏和标题栏重叠的几种方法原理都是一致的:让布局中的标题栏有一个状态栏高度的marginTop或者paddingTop即可。但因为4.4之前是不存在状态栏重合这种问题,所以也就不需要有一个状态栏高度的marginTop或者paddingTop,所以要考虑一个兼容性问题。

几种解决办法:

  1. 使用系统提供的fitSystemWindows属性
  2. 给标题控件动态增加paddingTop
  3. 在DecorView中动态创建一个状态栏高度的View,然后更改View背景为想要的颜色,然后设置标题栏marginTop为状态栏高度
  4. 在布局中定义一个假的用来充当状态栏的View,动态设置其高度或者可见性
    不同方式都有各自的优劣,建议根据不同的场景来灵活应用。

方式一:系统提供的fitSystemWindows属性

fitsSystemWindows属性:

  1. 在透明状态栏或者全屏时候生效。(4.4之前添加了这个属性也没有任何效果的,满足兼容性)
  2. 如果将某个View的该属性设置为true,则系统会为该View添加一个paddingTop,值为状态栏的高度。
  3. 如果多个View同时设置该属性为true,则只有第一个会起作用。此为一般情况。
  4. 默认系统只处理第一个fitsSystemWindows,但每个控件自己可以单独处理fitsSystemWindows。

fitsSystemWindows属性为true的时候,在透明状态栏模式下,4.4(含)之后的版本控件,系统会针对扫描到的该属性设置为true的第一个控件设置一个状态栏高度的paddingTop(原有View的paddingTop将会失效)。

知道上面的原理后,我们可以根据需要来为哪个控件设置fitsSystemWindows属性为true。

因为是设定paddingTop,如果你的标题栏是一个固定高度比如50dp,那么设置后是不会有效的,毕竟高度写死了的。所以呢通常设置这个属性的View高度是一个wrap_content,如果不是的话呢,那么你就在外面包一个FrameLayout或者LinearLayout,然后将原来View的背景设置到外面的包装Layout上。

比如下面的在原来标题栏RelativeLayout布局外面包装一个LinearLayout,然后给LinearLayout添加一个fitsSystemWindows属性,并且修改LinearLayout的背景为标题栏背景色。

对了,我试过fitsSystemWindows如果在Fragment中使用貌似是不生效的。

方式二:给标题栏控件动态增加paddingTop

4.4之前的话就不加了。可以用代码来动态增加,或者适用的话也可以放到values文件中。代码获取状态栏高度:

 public static int getStatusBarHeight() {        Resources resources = Resources.getSystem();        int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");        return resources.getDimensionPixelSize(resourceId);    }

给标题栏控件动态增加高度的方法:

public static void fitTitleBar(Activity activity, final View titleBar) {        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {            return;        }        makeStatusBarTransparent(activity);        final ViewGroup.LayoutParams layoutParams = titleBar.getLayoutParams();        assert layoutParams != null;        if (layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT ||                layoutParams.height == ViewGroup.LayoutParams.MATCH_PARENT) {            titleBar.post(new Runnable() {                @Override                public void run() {                    layoutParams.height = titleBar.getHeight() + getStatusBarHeight();                    titleBar.setPadding(titleBar.getPaddingLeft(),                            titleBar.getPaddingTop() + getStatusBarHeight(),                            titleBar.getPaddingRight(),                            titleBar.getPaddingBottom());                }            });        } else {            layoutParams.height += getStatusBarHeight();            titleBar.setPadding(titleBar.getPaddingLeft(),                    titleBar.getPaddingTop() + getStatusBarHeight(),                    titleBar.getPaddingRight(),                    titleBar.getPaddingBottom());        }    }

如果不是固定高度的控件,那么直接增加一个状态栏高度的paddingTop,如果是写死高度的控件,则需要同时给高度增加一个状态栏高度。

在应用此方法的时候要注意,这个titleBar控件增加了paddingTop或者高度后,其内部布局要非常协调,不要影响到美观了。如果你的标题栏用RelativeLayout写的,然后里面文本居中那么使用此方法后显示效果会很丑陋,可以参考第一种方式在外面包装一层。

除了代码控制,还可以根据API版本不同配置不同像素高度的status_bar_offset,然后设置到布局中。通常,状态栏高度是24或者25dp。在4.4(API 19)之前不需要处理重叠问题所以设置status_bar_offset为0dp,在values-v19中设置status_bar_offset为25dp,然后根据布局的不同动态增加padding。

比如:
values/dimens.xml

<?xml version="1.0" encoding="utf-8"?>    0dp

values-v19/dimens.xml

<?xml version="1.0" encoding="utf-8"?>    25dp

布局文件

                                

除了上面这种写法,还有很多种写法,发挥你的想象力!

根据版本不同处理不同的padding这种,个人还是比较推崇的,不用写太多代码,直接布局就写好了。当然可能是针对一些比较简单的页面处理哈。

方式三:动态创建状态栏高度的View添加到DecorView中,然后设置标题栏的marginTop为状态栏高度

很多第三方库都喜欢这种方式。个人不喜欢用,个人钟爱第二种和第一种哈哈。
这种方式也有局限性,比如对于DrawerLayout的情况,如果采用这种方式将statusBar添加到DecorView上,会导致侧边栏也会被状态栏所覆盖。

public static View setStatusBarColor(Activity activity, int color) {    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {          return null;    }    makeStatusBarTransparent(activity);    return applyStatusBarColor(activity, color);}private static View applyStatusBarColor(Activity activity, int color) {    View fakeStatusBar = ensureStatusBar(activity);    fakeStatusBar.setBackgroundColor(color);    return fakeStatusBar;}private static View ensureStatusBar(Activity activity) {    ViewGroup parent = (ViewGroup) activity.getWindow().getDecorView();    View fakeStatusBar = parent.findViewWithTag(TAG_FAKE_STATUS_BAR);    if (fakeStatusBar == null) {        fakeStatusBar = createStatusBar(activity);        fakeStatusBar.setTag(TAG_FAKE_STATUS_BAR);        parent.addView(fakeStatusBar);    }    return fakeStatusBar;}private static View createStatusBar(Activity activity) {    View statusBarView = new View(activity);    ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight());    statusBarView.setLayoutParams(layoutParams);    return statusBarView;}public static void addMarginTopEqualStatusBarHeight(View view) {    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {          return;    }    view.setTag(TAG_OFFSET);    Object haveSetOffset = view.getTag(KEY_OFFSET);    if (haveSetOffset instanceof Boolean) {        return;    }    ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();    layoutParams.setMargins(layoutParams.leftMargin,              layoutParams.topMargin + getStatusBarHeight(),              layoutParams.rightMargin,              layoutParams.bottomMargin);    view.setTag(KEY_OFFSET, true);}  public static void subtractMarginTopEqualStatusBarHeight(View view) {        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;        Object haveSetOffset = view.getTag(KEY_OFFSET);        if (haveSetOffset == null || !(Boolean) haveSetOffset) {            return;        }        ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();        layoutParams.setMargins(layoutParams.leftMargin,                layoutParams.topMargin - getStatusBarHeight(),                layoutParams.rightMargin,                layoutParams.bottomMargin);        view.setTag(KEY_OFFSET, false);    }

方式四 在布局中定义假的充当状态栏的View,然后动态设置其高度。

这种方式是为了解决方式三不适用于DrawerLayout的情况,因为DecorView中添加的View是盖在整个布局上的,所以会遮挡侧边栏。那么将其移动到布局中自己可控就好勒。

具体代码不提供了,有思路就好。

状态栏背景颜色为浅色导致状态字体看不清的问题解决

状态栏颜色默认为白色,如果你的状态栏背景为浅色,那么会导致字体看不清楚。Android 6.0之后提供了方法设置为深色字体。

下面设置为light模式,则是让状态栏字体颜色变深,反之变浅。

 public static void setStatusBarLightMode(Activity activity, boolean isLightMode) {        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {            Window window = activity.getWindow();            int option = window.getDecorView().getSystemUiVisibility();            if (isLightMode) {                option |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;            } else {                option &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;            }            window.getDecorView().setSystemUiVisibility(option);        }    }

更多相关文章

  1. 从零开始--系统深入学习android(实践-让我们开始写代码-指南-1.He
  2. [UI控件问题] android屏幕自适应 android 属性
  3. Android之根布局动态加载子布局时边距设置无效问题
  4. Android(安卓)实现沉浸式状态栏
  5. android中在Layout布局文件中添加控件ID,在Activity中findViewByI
  6. Android——实现全国省市区地区选择
  7. Android中如何提高UI的性能
  8. Android(安卓)开发环境配置+环境变量+模拟器上的调试设置Dev Too
  9. Android构建音频播放器教程(一)

随机推荐

  1. Android(安卓)Framework 基础理解
  2. Android(安卓)EditView属性
  3. 转载 android 利用ksoap2方式连接webserv
  4. android UI小结(二)
  5. 初探Android
  6. 【Android(安卓)系统开发】 编译 Android
  7. android开发初学建议
  8. Android(安卓)开发小技巧
  9. Android高斯模糊、高斯平滑(Gaussian Blur
  10. 如何发布你的Android应用程序