沉浸式体验

Managing the System UI

沉浸式体验,系统UI:状态栏(status bar)、导航栏(navigation bar)

system bars 包括 status bar 和 navigation bar,在播放电影或者预览一张照片时,为了能够使用户能够身临其境、沉浸式的体验,可以暂时的弱化系统 bar 的影响,避免分散我们的注意力。

为了使用沉浸式的体验效果,需要注意版本兼容

[TOC]

Dimming theSystem Bars

减弱 system bar 的显示效果,这种方法不会改变内容区(content view)的大小,但是 system bar 上的图标会减弱,当我们触碰到 status bar 或者是 navigation bar 的区域时,他们
都会完全显示出来,这种方法的优点是保留了 status bar 和 navigation bar 但是减弱了他们的细节。

注意此方法支持的版本是 Android 4.0(API 14以上

使用的 FLAG 是:View.SYSTEM_UI_FLAG_LOW_PROFILE,具体的减弱效果是减少 status bar 上的图标(navigation bar 应该也会有变化,但是没有这种设备没测出来),并且使剩下的图标变暗,当点击 home 键再切回来时,已经没有效果了。

使用方法如下:

1
2
3
4
// This example uses decor view, but you can use any visible view.
View decorView = getActivity().getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_LOW_PROFILE;
decorView.setSystemUiVisibility(uiOptions);

一旦用户触摸到 status bar 或 navigation bar 时,FLAG 就会清除,bar 上的减弱效果就会消失,一旦标志位被清除,需要重新启动 app 才能看到效果。

手动清除标志位:

1
2
3
4
View decorView = getActivity().getWindow().getDecorView();
// Calling setSystemUiVisibility() with a value of 0 clears
// all flags.
decorView.setSystemUiVisibility(0);

Hiding the Status Bar

隐藏状态栏、导航栏,让内容区使用更多的空间,时沉浸式的体验更深,隐藏状态栏的时候往往也需要把 action bar 也给隐藏。

Android 4.0 (API 14) 以下隐藏状态栏或导航栏

可以使用 WindowManager flags 或者设置 activity theme 来隐藏,如果状态栏需要总是保持隐藏状态的话,推荐在 manifest 中设置 theme 来实现。例如:

1
2
3
4
5
<application
...
android:theme="@android:style/Theme.Holo.NoActionBar.Fullscreen" >
...
</application>

使用主题优势是:

  • 比起使用代码手动设置更简单也更不易出现错误
  • 界面转化更加平滑,因为系统更早知道渲染 UI 的信息

使用 WindowManager flag 来实现,这种方法在于和用户交互来显示或者隐藏 status bar 的时候更加的灵活和简单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// If the Android version is lower than Jellybean, use this call to hide
// the status bar.
if (Build.VERSION.SDK_INT < 16) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
setContentView(R.layout.activity_main);
}
...
}

当我们使用 WindowManager 标志位的时候(包括使用 theme),flag 的效果会一直保持除非我们清楚标志位,在 FLAG_FULLSCREEN enabled 情况下我们也可以使用 FLAG_LAYOUT_IN_SCREEN 来设置 activity layout 使用同样的屏幕区域,这样还能防止内容区因为 status bar 的显示和隐藏进行缩放。

4.1及其以上隐藏状态栏

在 Android 4.1(API 16) 及其以上可以通过 setSystemUiVisibility() 通过设置对应的标志来实现显示和隐藏状态栏,并且 setSystemUiVisibility() 这个方法可以通过一些单独的 view 来调用,最终聚合到 window 级别上,下面的代码是隐藏 status bar:

1
2
3
4
5
6
7
8
View decorView = getWindow().getDecorView();
// Hide the status bar.
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
// Remember that you should never show the action bar if the
// status bar is hidden, so hide that too if necessary.
ActionBar actionBar = getActionBar();
actionBar.hide();

需要注意的点:

  1. 一旦 UI flags 被清除,如果我们需要继续隐藏状态栏需要重新设置,后面会提到怎么监听 UI visibility 的变化。
  2. 在不同的地方调用是有差别的,如果我们在 onCreate() 设置隐藏状态栏,用户按下 home 键后,system bar 的标志位会重置,用户再次打开 app, 不再执行 onCreate() 方法的时候,隐藏 status bar 就会失效,如果我们想让 system UI 的变化能够持久,在 app 切出去再回来的时候仍然有效的时候,我们应该在 onResume() 里面设置或者是在 onWindowFocusChanged()里。
  3. 调用 setSystemUiVisibility() 的 View 必须是可见的才有效果。
  4. 从当前 view 切出去会导致 setSystemUiVisibility() 标记位重置。

Make Content Appear Behind the Status Bar

在 Android 4.1 之后,我们可以设置让 app 的内容区在status bar 显示后再显示,这样内容区的大小不会因为 status bar 的显示或者隐藏而导致大小发生变化,要做到这一点,需要使用 SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN,同时也需要设置 SYSTEM_UI_FLAG_LAYOUT_STABLE 来让 app 保持一个稳定的布局。

当我们使用这个方法的时候,我们需要自己去确定系统 UI 的临界部分,大部分情况下可以在 XML 中增加 android:fitSystemWindows 属性设置为 true,也可以在代码中设置,来调整 parent ViewGroup 距离 system widows 的 padding。

Hiding the Navigation Bar

隐藏导航栏的方法在 Android 4.0(API 14)中引进,隐藏导航栏应该与隐藏状态栏一起使用,这样可以让内容区使用更完全的屏幕空间,使用方法与隐藏状态栏差不多:

1
2
3
4
5
6
7
8
View decorView = getWindow().getDecorView();
// Hide both the navigation bar and the status bar.
// SYSTEM_UI_FLAG_FULLSCREEN is only available on Android 4.1 and higher, but as
// a general rule, you should design your app to hide the status bar whenever you
// hide the navigation bar.
int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);

注意的点:

  1. 这样使用,点击屏幕的任何地方,status bar 和 navigation bar 就会出现并且保持显示状态,标志位会被清除。
  2. 一旦标志位被清除,需要重新设置
  3. onCreate()、onResume() 中使用情况不一样。
  4. 调用的 View 是可见的才有效。

让内容区在 navigation bar 后显示和内容区不改变大小,可以配合 SYSTEM_UI_FLAG_HIDE_NAVIGATION 和 SYSTEM_UI_FLAG_STABLE 标志使用。

Using Immersive Full-Screen Mode

全屏沉浸式模式,在 Android 4.4(API 19)中引进了一个新的 flag: SYSTEM_UI_FLAG_IMMERSIVE,可以让我们的 app 实现真正的全屏体验,结合 SYSTEM_UI_FLAG_HIDE_NAVIGATION 和 SYSTEM_UI_FLAG_FULLSCREEN 标志位的时候,可以隐藏状态栏和导航栏,并且响应整个屏幕的触摸事件。

看一下别人的翻译

Responding to UI Visibility Changes

监听 System UI visibility 的变化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
View decorView = getWindow().getDecorView();
decorView.setOnSystemUiVisibilityChangeListener
(new View.OnSystemUiVisibilityChangeListener() {
@Override
public void onSystemUiVisibilityChange(int visibility) {
// Note that system bars will only be "visible" if none of the
// LOW_PROFILE, HIDE_NAVIGATION, or FULLSCREEN flags are set.
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
// TODO: The system bars are visible. Make any desired
// adjustments to your UI, such as showing the action bar or
// other navigational controls.
} else {
// TODO: The system bars are NOT visible. Make any desired
// adjustments to your UI, such as hiding the action bar or
// other navigational controls.
}
}’’’’’’’”””
});

相关的标记位:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static final int SYSTEM_UI_FLAG_FULLSCREEN = 4;
public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 2;
public static final int SYSTEM_UI_FLAG_IMMERSIVE = 2048;
public static final int SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 4096;
public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 1024;
public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 512;
public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 256;
public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 8192;
public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 1;
public static final int SYSTEM_UI_FLAG_VISIBLE = 0;
public static final int SYSTEM_UI_LAYOUT_FLAGS = 1536;

参考链接

官方文档
Activity 全屏,沉浸式模式这一篇就够了