android 沉浸式解析

概述

首先我们得了解什么是透明状态栏以及什么是沉浸式状态栏,以及其区别,国内习惯称变色状态栏为沉浸式状态栏,但是两者是有本质区别的:

  • 沉浸式:
    Android 4.4中,沉浸式体验得到了再次强化,提供了一种“全屏模式”(Full-screen Immersive Mode)。全屏模式又分两种,一种叫后撤式 (Lean Back),另一种叫做沉浸式(Immersive)。后撤式已经在之前的系统中被广泛使用了——当你在优酷APP中观看视频时,大部分时间手指是不会去碰屏幕的。这种情况下,虚拟键和状态栏都会自动隐藏,但当你触摸屏幕的时候,它们又会出现。而新加入的沉浸式则不太一样,在沉浸式全屏状态下,对屏幕的操作并不会唤出系统栏。想要唤出系统栏,你必须从屏幕的上/下边缘向屏幕内划入。沉浸式的全屏状态更适合游戏和阅读这样的应用。
  • 变色:
    • 透明:
      Android 4.4 一个很重要的改变就是透明系统栏.。新的系统栏是渐变透明的, 可以最大限度的允许屏幕显示更多内容, 也可以让系统栏和 Action Bar 融为一体, 仅仅留下最低限度的背景保护以免通知通知栏内容和 Action Bar 文字/图标难以识别。谷歌把这种效果称之为:Translucent Bar。
      Translucent Bar 是 Android 对 Edge to Edge 尝试中的一个, 也是最容易被用户注意到的. 它的初始目的就是要最大化可视面积和淡化系统界面的存在感。
    • 变色:
      Android 5.0后可以自由设置状态栏颜色,所以也可以称作变色状态栏。

所以我们这里讲的是变色状态栏,变色状态栏就是如何对 StatusBar(状态栏)的背景透明操作;当我们打开应用时,一般如果不对状态栏进行修改的话,在屏幕的顶部有一条默认的(黑色、灰色、其他)状态栏,和应用的风格非常不协调,为了提供更好的界面交互,然后在状态栏的位置显示我们自定义的颜色,通常为应用的TitleBar的颜色,或者是将应用的整体的一张图片也占据到状态栏中,这个时候我们就需要对状态栏的背景进行操作。
状态栏详解点击

StatusBar、TitleBar

StatusBar是状态栏,它处于屏幕的最顶部,正常情况下它是显示的,它和TitleBar之间没有直接的关系;

TitleBar是标题栏,比如ActionBar、ToolBar,紧位于状态栏的下方显示。

ActionBar(操作栏) 是在Android 3.0(API 11)中加入到SK中的。

ToolBar(工具栏)是 Android 5.0 推出的一个 Material Design 风格的导航控件 ,用来取代之前的 Actionbar 。与 Actionbar 相比,Toolbar 明显要灵活的多。它不像 Actionbar 一样,一定要固定在Activity的顶部,而是可以放到界面的任意位置。与ActionBar相比显示效果跟ActionBar并没有区别。但优点是自定义视图的操作更加简单:

  • 1.设置导航栏图标;
  • 2.设置App的logo;
  • 3.支持设置标题和子标题;
  • 4.支持添加一个或多个的自定义控件;
  • 5.支持Action Menu;

ActionBar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<style name="AppCompatActivity_ActionBarTheme" 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>

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBar actionBar = getSupportActionBar();
actionBar.setTitle("ActionBar Title");
actionBar.setBackgroundDrawable(getDrawable(R.drawable.ic_launcher_background));
actionBar.setCustomView(R.layout.activity_actionbar);
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
}

备注是getSupportActionBar()还是默认的getActionBar(),还可以自定义title view

Toolbar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<style name="AppCompatActivity_ToolBarTheme" parent="Theme.AppCompat.Light">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:textAllCaps">false</item>
</style>

<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:orientation="vertical"
android:layout_width="match_parent"
android:background="?attr/colorPrimary"
android:layout_height="200dp"
app:title="标题"
android:minHeight="?attr/actionBarSize">
<TextView
android:id="@+id/toolbar_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ff0000"
android:text="自定义View"/>
</android.support.v7.widget.Toolbar>

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//也可以这里设置toolbar
Toolbar toolBar = findViewById(R.id.toolbar);
//主标题,必须在setSupportActionBar之前设置,否则无效,如果放在其他位置,则直接setTitle即可
toolBar.setTitle("ToolBar Title");
//用toolbar替换actionbar
setSupportActionBar(toolBar);
}

备注可以在xml设置属性,也以在代码中设置;还可以在xml添加view;还可以对Toolbar自定义;使用Toolbar要隐藏ActionBar;

ActionBar和ToolBar:使用详细点击

item属性释义

item
item

比较通用的几个属性,引用了网上的一个截图;更多详细见:
各属性指定颜色的位置

状态栏背景色

Android4.4(< API 19)

在Android系统4.4以前,状态栏的背景色和字体颜色都是不能改变的,我们可以对 StatusBar 进行全屏或隐藏操作。

  • 配置theme
    1
    android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
  • 代码全屏
    1
    2
    getWindow().setFlags(WindowManager.LayoutParams. FLAG_FULLSCREEN ,
    WindowManager.LayoutParams. FLAG_FULLSCREEN);

Android4.4(API 19)- Android 5.0(< API 21)

  • theme:
    1
    2
    3
    4
    <item name="android:windowTranslucentStatus">true</item>

    <!-- 失效 -->
    <item name="colorPrimaryDark">@color/colorPrimary</item>
  • 代码:
    1
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

设置如上属性后,我们会发现状态栏的背景变了,其实不是变了,而是标题栏的布局延伸到了状态栏里。

如果是图片浸入的话:直接给TitleBar或者自定义view设置背景图即可。

1
2
Toolbar toolBar = findViewById(R.id.toolbar);
toolBar.setBackgroundResource(R.mipmap.title_back);

Android 5.0(>= API 21)

  • theme:
    1
    <item name="colorPrimaryDark">@color/colorPrimary</item>
  • 代码:
    1
    getWindow().setStatusBarColor(getResources().getColor(android.R.color.holo_red_light));

如果是图片浸入的话:直接在主题中设置状态栏背景色透明即可,同理直接给TitleBar或者自定义view设置背景图即可;效果图同上。

1
2
3
4
5
<item name="android:windowTranslucentStatus">true</item>
<item name = "android:statusBarColor">@android:color/transparent</item>

Toolbar toolBar = findViewById(R.id.toolbar);
toolBar.setBackgroundResource(R.mipmap.title_back);

android:fitsSystemWindows=”true”

当我们设置了android:windowTranslucentStatus=true后,我们发现Toolbar内容被StatusBar覆盖,相当于Toolbar整体布局上移了,怎么解决?我们可以设置android:fitsSystemWindows=”true”,但是这时候我们发现,我们的StatusBar的背景色实效了。如何解决?我们就可以通过往Window窗口的decorView添加一个View,让它大小与系统状态栏一样,然后设置这个view的背景,就可以实现修改状态栏颜色的效果了。但是这种只适合修改StatusBar的背景色,如果是图片浸入,就不可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ViewGroup decorViewGroup = (ViewGroup) getWindow().getDecorView();
View statusBarView = new View(getWindow().getContext());
int statusBarHeight = getStatusBarHeight(getWindow().getContext());
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, statusBarHeight);
params.gravity = Gravity.TOP;
statusBarView.setLayoutParams(params);
statusBarView.setBackgroundResource(R.color.colorPrimary);
decorViewGroup.addView(statusBarView);

private static int getStatusBarHeight(Context context) {
int statusBarHeight = 0;
Resources res = context.getResources();
int resourceId = res.getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
statusBarHeight = res.getDimensionPixelSize(resourceId);
}
return statusBarHeight;
}

TitleBar图片浸入

我们从上面的效果图,可以看出,图片浸入时,相当于TitleBar移动到了状态栏的下面,这个时候我们标题可能与状态栏的图标重叠,那怎么办呢?上边说了android:fitsSystemWindows=”true”不行,需要设置状态栏背景色失效,其实我们这个时候,可以在TitleBar上添加一个和状态栏大小的透明view即可,TitleBar设置paddingTop 也可以,但是你需要重新设置TitleBar的高度,这样的话就不如第一种方案合适了。
设计一个xml,这个包含了TitleBar和默认的StatusBar的替代view。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/toolbar"
android:layout_alignTop="@+id/statusBartop"
android:scaleType="fitXY"
android:src="@mipmap/title_back" />

<LinearLayout
android:id="@+id/statusBartop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#00000000"
android:orientation="horizontal" />

<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/statusBartop"
>

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text=" dkfslfjslfsjflsjflsf"
android:textColor="@android:color/holo_red_dark"

android:textSize="16dp" />
</android.support.v7.widget.Toolbar>
</RelativeLayout>

然后我们在代码中进行设置即可:版本大于18,即4.4以上即可,4.4一下不存在沉浸式模式;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int sysVersion = Build.VERSION.SDK_INT;
if (sysVersion > Build.VERSION_CODES.JELLY_BEAN_MR2 ) {
int result = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
LinearLayout toptop = this.findViewById(R.id.statusBartop);
RelativeLayout.LayoutParams para = new RelativeLayout.LayoutParams(this.getWindowManager().getDefaultDisplay().getWidth(), result);
//设置修改后的布局。
toptop.setLayoutParams(para);

Toolbar toolbar = this.findViewById(R.id.toolbar);
RelativeLayout.LayoutParams paraBar = new RelativeLayout.LayoutParams(this.getWindowManager().getDefaultDisplay().getWidth(), 150);
paraBar.addRule(RelativeLayout.BELOW,R.id.statusBartop);
toolbar.setLayoutParams(paraBar);
}

最后别忘记设置

1
2
3
<item name="android:windowTranslucentStatus">true</item>
或者在代码中:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

看下效果图: