随着Android版本的迭代更新,如何使某项特性能够兼容各个版本,一直困扰开发人员。比如状态栏(Status Bar)的显示与隐藏,本篇文章也主要讨论Android中状态栏(Status Bar)以及导航栏(Navigation Bar)的显示与隐藏。
在Android4.0(API level 14)及以下你可以通过设置 WindowManager 的标志来隐藏Status Bar,你可以通过在代码中设置或者通过在manifest中设置activity的主题来实现。如果在你的APP中Status Bar需要一直隐藏,那么最好的方法是在manifest中设置activity或者application的主题。
...
当然,你也可以在代码中设置 WindowManager 的标志来实现。这种方式使得当程序需要根据用户的交互来显示或者隐藏Status Bar变得很容易。
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 的标志时,它会一直起作用,除非你清除这些标志。
那么问题来了,当我们因为需要隐藏Status Bar而激活FLAG_FULLSCREEN标志时,如果来清除此标记使得Status Bar重新显示出来。这个时候我们可以通过设置 FLAG_LAYOUT_IN_SCREEN 标记来使Status Bar重新显示,这也避免了你的布局在Status Bar显示或隐藏时需要重新调整大小。
在Android4.1(API level 16) 及以上隐藏Status Bar可以调用 setSystemUiVisibility()方法,该方法在一个独特的视图水平设置UI的标记,这些设置会聚集到window层级,相比设置WindowManager 的标记,使用这个方法可以让你对Status Bar有更细粒度的控制。
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标记被清除(比如离开当前activity),如果你想继续隐藏Status Bar,你需要重新设置他们。
2.在哪个位置设置UI标记很重要。如果你在 onCreate()
方法中设置,当用户点击home键时,此时Status Bar会出现,当用户再次打开该activity时, onCreate()
方法不会被调用,导致Status Bar会一直显示。如果你想在用户导航到当前activity或者导航出当前activity时一直保持系统UI的改变,你应该在 onResume()
或者 onWindowFocusChanged()
.中设置UI标记。
3.当你离开当前视图时,通过该方法设置的UI标记会被清除。
在Android 4.1 及以上你可以设置你的应用程序的内容显示在Status Bar的后面,这样一来,当Status Bar显示或者隐藏时,你的内容不仅就不需要重新调整大小了。要达到这样的效果使用 SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
标记。你可能同时需要使用 SYSTEM_UI_FLAG_LAYOUT_STABLE来帮助你的app持有一个稳定的布局。
在大多数情况下,你可以通过在你的布局xml文件中添加一个标记android:fitsSystemWindows
,并设置为true,这会导致系统窗口的父布局调整为系统窗口留的padding。这种方式对大多数app有效。
然而,在某些情况下,你需要更改默认的padding,来直接操作你的布局相对于system bars如何放置,通过覆写fitSystemWindows(Rect insets)方法。
你可以使用 SYSTEM_UI_FLAG_LOW_PROFILE
标记来dim Status and Navigation Bars,如下:
// 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);
如果你想在代码里清除所有使用setSystemUiVisibility()
方法设置的标记,可以按照如下方式:
View decorView = getActivity().getWindow().getDecorView();
// Calling setSystemUiVisibility() with a value of 0 clears
// all flags.
decorView.setSystemUiVisibility(0);
隐藏导航栏(Android 4.0 (API level 14))及以上引入,可以使用 SYSTEM_UI_FLAG_HIDE_NAVIGATION
标记来隐藏导航栏,如下代码:
View decorView = getWindow().getDecorView();
// 同时隐藏 navigation bar 和 status bar.
// SYSTEM_UI_FLAG_FULLSCREEN 标记仅仅存在于 Android 4.1 及以上, 但是当你隐藏navigation bar时也应该隐藏status bar
int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
1.通过这种方式,触摸屏幕的任何地方都会导致navigation bar (and status bar) 出现并且一直显示,用户的交互使得标记被清除。
2.一旦标记被清除,需要重新设置。
3.在哪个位置设置UI标记很重要。如果你在 onCreate()
方法中设置,当用户点击home键时,此时Status Bar会出现,当用户再次打开该activity时, onCreate()
方法不会被调用,导致Status Bar会一直显示。如果你想在用户导航到当前activity或者导航出当前activity时一直保持系统UI的改变,你应该在 onResume()
或者 onWindowFocusChanged()
.中设置UI标记。
4.当你离开当前视图时,通过该方法设置的UI标记会被清除。
SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
标记。你可能同时需要使用
SYSTEM_UI_FLAG_LAYOUT_STABLE
来
帮助你的app持有一个稳定的布局。
在Android 4.4 (API Level 19)为setSystemUiVisibility()
引进了一个新的标记 SYSTEM_UI_FLAG_IMMERSIVE,来使你的app真正的全屏。当这个标记与
SYSTEM_UI_FLAG_HIDE_NAVIGATION
和 SYSTEM_UI_FLAG_FULLSCREEN
结合使用,隐藏 navigation 和 status bars并且让你的app能够捕获屏幕上的所有触摸事件。
当沉浸式全屏模式启用,你的activity任然可以接收所有的触摸事件。用户可以在system bars本来在的位置向内滑动从而使system bars显示出来。当然这会导致标记 SYSTEM_UI_FLAG_HIDE_NAVIGATION
(如果设置了SYSTEM_UI_FLAG_FULLSCREEN也会被清除)会被清除。如果设置了监听器 View.OnSystemUiVisibilityChangeListener
,此时会触发这个监听器。当然,如果你想让system bars 隔一会后自动隐藏,你应该使用SYSTEM_UI_FLAG_IMMERSIVE_STICKY
标记。注意,该标记不会触发任何监听器。
// This snippet hides the system bars.
private void hideSystemUI() {
// Set the IMMERSIVE flag.
// Set the content to appear under the system bars so that the content
// doesn't resize when the system bars hide and show.
mDecorView.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 // hide nav bar
| View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
| View.SYSTEM_UI_FLAG_IMMERSIVE);
}
// This snippet shows the system bars. It does this by removing all the flags
// except for the ones that make the content appear under the system bars.
private void showSystemUI() {
mDecorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
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);}
}
要监听系统UI的变化,需要注册监听器,如下所示:
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.
}
}
});