安卓透明状态栏Translucent bar与沉浸式,修改状态栏颜色

时间:2024-03-09 09:00:58

工程目录如下,也是需要的三个文件:对应着不同的版本,让界面占满整个屏幕

values/styles.xml

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <style name="TranslucentTheme" parent="AppTheme">
        <!--在Android 4.4之前的版本上运行,直接跟随系统主题,也就是不能控制状态栏的颜色了-->
    </style>

</resources>

values-v19/styles.xml

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


    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <!-- 透明的主题,让界面填满整个屏幕 ,v19对应安卓4.4-->
    <style name="TranslucentTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <!--状态栏 让显示时间的那栏透明(就是去掉)-->
        <item name="android:windowTranslucentStatus">true</item>
        <!--导航栏 三个虚拟按键的那栏,,透明(就是去掉)  false就是显示-->
        <item name="android:windowTranslucentNavigation">true</item>
    </style>

</resources>

values-v21/styles.xml

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

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <!-- 透明的主题 -->
    <style name="TranslucentTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <!--状态栏 让显示时间的那栏透明(就是去掉)-->
        <item name="android:windowTranslucentStatus">true</item>
        <!--导航栏 三个虚拟按键那栏,透明(就是去掉)  false就是显示-->
        <item name="android:windowTranslucentNavigation">true</item>
        <!--android5.X开始需要把把颜色设置为透明,否则导航栏会呈现系统默认的浅灰色-->
        <item name="android:statusBarColor">@android:color/transparent</item>
    </style>

</resources>

使用:在你需要的activity上进行主题的设置就可以了

 <!--设置activity的主题为自定义透明的主题,就能实现占满整个屏幕的效果了-->
        <activity android:name=".MainActivity"
            android:theme="@style/TranslucentTheme">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

然后在布局中设置背景颜色

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:background="#ff0000"
    android:orientation="vertical">
    <!--fitsSystemWindows="true"  设置了该属性的作用在于,-------注意点
        防止我们的控件内容显示到状态栏上(显示时间的那栏)
        注意,在这里设置的背景颜色,会让总个页面都是这个颜色-->

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="文本"
        android:background="#ffcccc"
        android:textSize="30sp" />

</LinearLayout>

效果如下:

 

对于上面几个颜色的说明:

1 syles.xml(定义了颜色属性)

2 模拟器运行效果图

 

 

像播放视频一样的全屏显示,且侧拉的时候能显示状态栏与导航栏可见:郭大神的博客,下面是整理了下的

http://blog.csdn.net/sinyu890807/article/details/51763825?locationNum=1

什么叫沉浸式?

根据百度百科上的定义,沉浸式就是要给用户提供完全沉浸的体验,使用户有一种置身于虚拟世界之中的感觉。

那么对应到Android操作系统上面,怎样才算是沉浸式体验呢?这个可能在大多数情况下都是用不到的,不过在玩游戏或者看电影的时候就非常重要了。因为游戏或者影视类的应用都希望能让用户完全沉浸在其中,享受它们提供的娱乐内容,但如果这个时候在屏幕的上方还显示一个系统状态栏的话,可能就会让用户分分钟产生跳戏的感觉.  一个安卓应用程序的页面上包含着许多系统元素

可以看到,有状态栏、ActionBar、导航栏等。而打造沉浸式模式的用户体验,就是要将这些系统元素全部隐藏,只留下主体内容部分。

 隐藏状态栏和ActionBar的方式在4.1系统上下是不一样的,因此不考虑4.1系统一下的兼容性,因为以前的系统没有提供沉浸式体验的支持

首先我们来实现全屏显示,布局文件如下(下面的布局都为这个):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="fanggao.qf.immersivetest.MainActivity">
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"
        android:src="@drawable/a"/>
</LinearLayout>

java代码:

根据Android的设计建议,ActionBar是不应该独立于状态栏而单独显示的,因此状态栏如果隐藏了,我们同时也需要调用ActionBar的hide()方法将ActionBar也进行隐藏。
注意:得到的actionBar必须是v7包向下兼容的,否则会空指针,找不到actionBar
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //得到当前界面的装饰视图
        View decorView = getWindow().getDecorView();
     //SYSTEM_UI_FLAG_FULLSCREEN表示全屏的意思,也就是会将状态栏隐藏
        //设置系统UI元素的可见性
        decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);
        //隐藏标题栏
        ActionBar actionBar = getSupportActionBar();
        actionBar.hide();
    }

 效果如下:

这样看上去就有点沉浸式效果的模样了。

虽说这才是正统的沉浸式含义,但如果我们想要实现饿了么那样的状态栏效果,而不是直接把整个系统状态栏给隐藏掉,那么又该如何实现呢?

其实也很简单,只需要借助另外一种UI Flag就可以了

 首先需要注意,饿了么这样的效果是只有5.0及以上系统才支持,因此需要if判断,只有系统版本大于或等于5.0的时候才会执行下面的代码。

注意下面的参数与上面的不同

上面: View.SYSTEM_UI_FLAG_FULLSCREEN
下面:View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
     //得到当前界面的装饰视图
        if(Build.VERSION.SDK_INT >= 21) {
            View decorView = getWindow().getDecorView();
            //让应用主题内容占用系统状态栏的空间,注意:下面两个参数必须一起使用 stable 牢固的
            int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
            decorView.setSystemUiVisibility(option);
            //设置状态栏颜色为透明
            getWindow().setStatusBarColor(Color.TRANSPARENT);
        }
        //隐藏标题栏
        ActionBar actionBar = getSupportActionBar();
        actionBar.hide();

效果: (如果是有导航栏的手机,这里不会隐藏)

 

使用的样式:  指定样式加上上面的java代码就ok了,如果不行在布局中加上 android:fitsSystemWindows="true"

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- All customizations that are NOT specific to a particular API-level can go here. -->
        <item name="colorPrimary">@color/pickerview_timebtn_nor</item>
        <item name="colorPrimaryDark">@color/pickerview_timebtn_nor</item>
        <item name="colorAccent">@color/pickerview_timebtn_nor</item>
    </style>

饿了么页面效果就出来了,但如果我们需要实现沉浸式的效果,光隐藏状态栏是不够的,还需要隐藏导航栏

修改代码如下: 放到Activity中的onCreate方法就行

    //得到当前界面的装饰视图
    if(Build.VERSION.SDK_INT >= 21) {
        View decorView = getWindow().getDecorView();
        //设置让应用主题内容占据状态栏和导航栏
        int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_HIDE_NAVIGATION|View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
        decorView.setSystemUiVisibility(option);
        //设置状态栏和导航栏颜色为透明
        getWindow().setStatusBarColor(Color.TRANSPARENT);
        getWindow().setNavigationBarColor(Color.TRANSPARENT);
    }
    //隐藏标题栏
    ActionBar actionBar = getSupportActionBar();
    actionBar.hide();

手机没有导航栏不做演示,但大致跟上图一样,只是下方有导航栏的几个按钮(背景已被设置为透明了)

但这并不是真正的沉浸式效果,我们在做沉浸式效果之前,必须考虑是否真正需要这个效果,因为只有向游戏或者视频这种需要给用户带来良好用户体验的应用程序才需要这种效果,需要注意的是,只有在Android 4.4及以上系统才支持沉浸式模式,因此这里也是加入了if判断

代码如下:

@Override
protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
   //getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);//设置成全屏模式
   //setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//竖屏
   //设置屏幕为横屏
     setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
     setContentView(R.layout.activity_main);

}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
     super.onWindowFocusChanged(hasFocus);
     //判断是否有焦点
     if(hasFocus && Build.VERSION.SDK_INT >= 19){
           View decorView = getWindow().getDecorView();
           decorView.setSystemUiVisibility(
                    View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
           );
      }
}

效果如下:

 

界面默认情况下是全屏的,状态栏和导航栏都不会显示。而当我们需要用到状态栏或导航栏时,只需要在屏幕顶部向下拉,或者在屏幕右侧向左拉,状态栏和导航栏就会显示出来,此时界面上任何元素的显示或大小都不会受影响。过一段时间后如果没有任何操作,状态栏和导航栏又会自动隐藏起来,重新回到全屏状态。这就是最标准的沉浸式模式。

 

修改状态栏的背景颜色:

/**
     * 设置状态栏颜色
     *
     * @param activity       需要设置的activity
     * @param color          状态栏颜色值  Color.parseColor("#ff000000")
     */
    public static void setStatusBarColor(Activity activity, int color) {
        //操作系统的api版本大于21,才能改变状态栏的颜色
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            //设置状态栏的颜色
            activity.getWindow().setStatusBarColor(color);

            //如果大于6.0  当状态栏颜色为亮色,就改变状态栏的字体颜色
            /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if(ColorUtils.calculateLuminance(color) >= 0.5){
                    activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);//亮黑色
                } else {
                    activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);//白色
                }
            }*/

        }
    }

当你需要多次设置状态栏文字颜色的时候,需要在之前的基础上进行修改:

//1、设置状态栏文字深色,同时保留之前的flag
int originFlag = activity.getWindow().getDecorView().getSystemUiVisibility();
activity.getWindow().getDecorView().setSystemUiVisibility(originFlag | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);

//2、清除状态栏文字深色,同时保留之前的flag
int originFlag = activity.getWindow().getDecorView().getSystemUiVisibility();
//使用取非 再与 清除SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
activity.getWindow().getDecorView().setSystemUiVisibility(originFlag & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);

 

比如当activity中多个fragment切换,更换状态栏字体颜色:

/**
     * 设置状态栏中字体颜色为亮色(黑色或者白色)
     * @param color  状态栏的颜色
     *               Color.BLACK: 更改为白色
     *               Color.WHITE: 更改为黑色
     */
    public static void setStatusBarFontLightColor(Activity activity,int color){
        //如果大于6.0  当状态栏颜色为亮色,就改变状态栏的字体颜色
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            int systemUiVisibility = activity.getWindow().getDecorView().getSystemUiVisibility();
            //清除SYSTEM_UI_FLAG_LIGHT_STATUS_BAR状态 与  SYSTEM_UI_FLAG_VISIBLE状态
            systemUiVisibility &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
            systemUiVisibility &= ~View.SYSTEM_UI_FLAG_VISIBLE;

            if(ColorUtils.calculateLuminance(color) >= 0.5){
                //设置成黑色
                activity.getWindow().getDecorView().setSystemUiVisibility(systemUiVisibility | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
            } else {
                //白色
                activity.getWindow().getDecorView().setSystemUiVisibility(systemUiVisibility | View.SYSTEM_UI_FLAG_VISIBLE);
            }
        }
    }

可以与上面沉浸式代码配合使用,注意需要先设置状态栏字体颜色,再设置沉浸式模式

activity的结构图:

可参考:

activity的布局结构

 

所以我们可以在BaseActivity中对FrameLayout做一些处理:

可参考:https://www.jianshu.com/p/dc20e98b9a90

在BaseActivity中调用下面两个方法:

paddingStatusView(this);//activity的布局设置padding值
addStatusViewWithColor(this,Color.parseColor("#ff0000"));//添加一个占位view

代码:

/**
     * @param activity  为activity的父布局加了个padding值,并且把背景色设置成了蓝色
     */
    private void paddingStatusView(Activity activity) {
        FrameLayout contentFrameLayout = activity.findViewById(android.R.id.content);
        contentFrameLayout.setBackgroundColor(Color.parseColor("#0000ff"));
        contentFrameLayout.setPadding(0, 2 * getStatusBarHeight(activity), 0, 0);
    }

    /**
     * 添加状态栏占位视图
     *
     * @param activity
     */
    private void addStatusViewWithColor(Activity activity, int color) {
        ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content);
        View statusBarView = new View(activity);
        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, getStatusBarHeight(activity));
        lp.setMargins(50,0,200,0);//便于区分,左右设置两个间距
        //statusBarView.setPadding(20,20,20,20);
        statusBarView.setBackgroundColor(color);
        statusBarView.setLayoutParams(lp);
        contentView.addView(statusBarView);
    }

    /**
     * 利用反射获取状态栏高度
     * @return
     */
    public int getStatusBarHeight(Activity activity) {
        int result = 0;
        //获取状态栏高度的资源id
        int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            result = activity.getResources().getDimensionPixelSize(resourceId);
        }
        return result;
    }

效果图:activity不是沉浸式状态,利用该方法 还可以为activity添加其他的view(比如红色区域)

 第一种与第二种的单独使用效果: