状态栏是顶部的电池电量、信号等显示的栏目。导航栏是底部的虚拟控制键栏目。
暗化状态栏导航栏
使用FLAG_LOW_PROFILE标志
// 使用decorView或者其他任何可见的View
View decorView = getActivity().getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_LOW_PROFILE;
decorView.setSystemUiVisibility(uiOptions);
设置后点击状态栏和导航栏即可使其恢复正常状态,并且清除这个标志。
隐藏状态栏
4.0及以下
4.0及以下的隐藏方式有两种,使用WindowManager隐藏或者在xml中设置。
activity中设置:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT < 16) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
setContentView(R.layout.activity_main);
}
...
}
可以同时添加 FLAG_LAYOUT_IN_SCREEN标志,避免因为状态栏的显示和隐藏导致布局跳变。
xml:
<application
...
android:theme="@android:style/Theme.Holo.NoActionBar.Fullscreen" >
...
</application>
xml设置比较容易维护不出错,而且在activity渲染之前就确定了状态栏隐藏,过渡比较流畅。
4.1开始
View decorView = getWindow().getDecorView();
// Hide the status bar.
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
ActionBar actionBar = getActionBar();
actionBar.hide(); // 隐藏状态栏的时候actionBar也应该隐藏起来
注意点:
- 如果标志清空了(比如离开了设置了标志的界面),就需要重新设置标志。
- 如果在onCreate()中设置全屏,点击Home键跳转出去,则状态栏会重新出现,再返回该界面时,由于onCreate()不会再次执行,所以状态栏保持可见,所以如果需要跳转回来仍不可见,则需要在onResume()或者onWindowFocusChanged()中设置标志。
- setSystemUiVisibility()只在调用该方法的View可见的情况下才生效,然后所有在view上设置的这些标志都会汇总到window级别改变系统ui。
- 跳转离开设置了标志的view后其设置的标志会被清除。
在状态栏处显示内容
4.1开始可以使用 SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN使布局充满原先状态栏所在位置,就不会因为状态栏的隐藏出现产生跳变。同时需要设置SYSTEM_UI_FLAG_LAYOUT_STABLE标志使布局稳定。
使用这种方式时,需要注意不要让布局被状态栏遮盖住,可以使用android:fitsSystemWindows属性,这会让父布局为系统状态栏留出padding空间。如果想要修改这个padding,可以调用 fitSystemWindows(Rect insets)方法来实现。
设置状态栏颜色
5.0(API21)开始可以通过设置Activity主题的两个属性实现透明状态栏或其他颜色。以下为继承应用主题的自定义主题。windowDrawsSystemBarBackgrounds为true是statusBarColor属性生效的条件。
<style name="AppTheme.TransparentStatusBar">
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
设置了以上代码之后还需要在xml中显示到状态栏区域的View及其各层父布局中都设置fitSystemWindow为true才能正常显示。
隐藏导航栏
View decorView = getWindow().getDecorView();
// 导航栏和状态栏最好同时隐藏
int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
注意点:
- 点击屏幕任意地方会清除标志,重新显示状态栏和导航栏。
- 设置时机很关键,onCreate()中设置和onResume()或者onWindowFocusChanged()是不一样的。
- 调用 setSystemUiVisibility()的View如果不可见则该方法不生效。
- 从设置标志的View跳转离开的话标志就会被清除。
- 设置内容显示在导航栏所在位置,与状态栏做法一致。
沉浸式模式基本用法
沉浸式模式与普通全屏模式的区别就是全屏模式点击屏幕就会退出全屏,沉浸式模式在顶部下拉时才会退出全屏,粘性沉浸式在顶部下拉时短暂出现透明状态栏后自动消失。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
uiOptions ^= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
uiOptions ^= View.SYSTEM_UI_FLAG_FULLSCREEN;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
uiOptions ^= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
}
getWindow().getDecorView().setSystemUiVisibility(uiOptions);
沉浸式的检测
int uiOptions = getWindow().getDecorView().getSystemUiVisibility();
// 检测当前是否沉浸式
boolean isImmersiveModeEnabled =
((uiOptions | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) == uiOptions);
各种标志详解:
- View.SYSTEM_UI_FLAG_LOW_PROFILE:使状态栏和导航栏上的一些图标变暗不可见,以降低用户注意力的分散。单独设置该标志时,点击状态栏和导航栏即可使其重新恢复,并且清除这个标志。
- View.SYSTEM_UI_FLAG_FULLSCREEN:隐藏非必要的UI,比如状态栏。隐藏状态栏后用户下滑即重新显示,当沉浸模式同时开启时,应用的可绘制区域扩大,下滑显示状态栏时,状态栏以半透明方式显示在app的上方。
- View.SYSTEM_UI_FLAG_HIDE_NAVIGATION:隐藏底部的导航栏。正常情况下用户点击屏幕后导航栏即可重新出现,在沉浸模式下,用户上滑才会显示出来。
- View.SYSTEM_UI_FLAG_IMMERSIVE:沉浸模式必须与以上至少一种标志合用才能生效,当用户把状态栏或者导航栏滑出来后即退出沉浸模式,该标志自动清除掉。
- View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY:粘性沉浸模式与普通沉浸模式的区别在于两点,一是滑出来的状态栏或导航栏呈半透明状态。二是滑出来的状态栏或者导航栏短时间后会自动隐藏,该标志也不会自动清除。
清空标志:
getWindow().getDecorView().setSystemUiVisibility(0);
模式区分
沉浸式体验应该设置三个标志FULL_SCREEN、HIDE_NAVIGATION和IMMERSIVE或者IMMERSIVE_STICKY,并且清除掉LOW_PROFILE标志
int uiOptions = flagsView.getSystemUiVisibility();
uiOptions &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE; // 清除标志
uiOptions |= View.SYSTEM_UI_FLAG_FULLSCREEN; // 添加标志
uiOptions |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
uiOptions |= View.SYSTEM_UI_FLAG_IMMERSIVE;
uiOptions &= ~View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
leanback模式:点击退出型沉浸式,普通全屏模式
int uiOptions = flagsView.getSystemUiVisibility();
uiOptions &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE;
uiOptions |= View.SYSTEM_UI_FLAG_FULLSCREEN;
uiOptions |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
uiOptions &= ~View.SYSTEM_UI_FLAG_IMMERSIVE;
uiOptions &= ~View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
flagsView.setSystemUiVisibility(uiOptions);
沉浸式优化
使用以下代码设置使布局出现在状态栏下方,所以沉浸式切换不会因为状态栏消失导致跳变,但是状态栏显示后会覆盖住布局内容:
int uiOptions = flagsView.getSystemUiVisibility();
uiOptions |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
uiOptions |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
uiOptions |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
flagsView.setSystemUiVisibility(uiOptions);
可以封装成下面的方法
// 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);
}
}
IMMERSIVE标志的应用
可以通过Handler.postDelay设置IMMERSIVE标志自行实现粘性沉浸式。
可以通过设置GestureDetector的onSingleTapUp(MotiveEvent)切换IMMERSIVE标志。
设置监听
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) {
// 状态栏可见
} else {
// 状态栏不可见
}
}
});