一、前期基础知识储备
状态栏 StatusBar:顶部的一条显示时间、电量、通知等信息的 bar
导航栏 NavigationBar:底部包含 back 键、home 键以及 recent 键的 bar
实际开发中,关于两个bar的处理还是很常见的,每个应用可能采用的设置也不尽相同,这里博主就列出常用的关于两个bar的设置:
1)状态栏隐藏不可见;
2)状态栏可见设为透明,随不同界面改变自己的状态;
3)导航栏隐藏不可见,手指沿手机底部上滑唤出导航栏;
4)导航栏可见设为透明,同时层级设为应用内容同级,以防覆盖住应用交互控件;
而为了实现这些效果,需要我们掌握和系统UI显示隐藏相关的各种Flag,每种Flag适用的场景!
API方法:
getWindow().getDecorView().setSystemUiVisibility(int options)
相关Flag:
WindowManager.LayoutParams.FLAG_FULLSCREEN
隐藏状态栏
View.SYSTEM_UI_FLAG_VISIBLE API 14
默认标记
View.SYSTEM_UI_FLAG_LOW_PROFILE API 14
低调模式, 会隐藏不重要的状态栏图标
View.SYSTEM_UI_FLAG_LAYOUT_STABLE API 16
保持整个View稳定, 常和控制System UI悬浮, 隐藏的Flags共用, 使View不会因为System UI的变化而重新layout
View.SYSTEM_UI_FLAG_FULLSCREEN API 16
状态栏隐藏,效果同设置WindowManager.LayoutParams.FLAG_FULLSCREEN
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN API 16
视图延伸至状态栏区域,状态栏上浮于视图之上
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION API 14
隐藏导航栏
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION API 16
视图延伸至导航栏区域,导航栏上浮于视图之上
View.SYSTEM_UI_FLAG_IMMERSIVE API 19
沉浸模式, 隐藏状态栏和导航栏, 并且在第一次会弹泡提醒, 并且在状态栏区域滑动可以呼出状态栏(这样会系统会清楚之前设置的View.SYSTEM_UI_FLAG_FULLSCREEN或View.SYSTEM_UI_FLAG_HIDE_NAVIGATION标志)。使之生效,需要和View.SYSTEM_UI_FLAG_FULLSCREEN,View.SYSTEM_UI_FLAG_HIDE_NAVIGATION中的一个或两个同时设置。
View.SYSTEM_UI_FLAG_IMMERSIVE_STIKY API 19
与上面唯一的区别是, 呼出隐藏的状态栏后不会清除之前设置的View.SYSTEM_UI_FLAG_FULLSCREEN或View.SYSTEM_UI_FLAG_HIDE_NAVIGATION标志,在一段时间后将再次隐藏系统栏)
转自《玩转Android上透明状态栏,全屏显示以及沉浸模式(Immersive Mode)》
二、上代码,具体实现
创建一个Activity,不对状态栏和导航栏做任何处理;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
效果如图:应用内布局,为了区分在顶部和底部各放置一个颜色为“#ADD8E6”的绿色色块,加以区分系统两个bar;可以看到应用内容此时在状态栏之下,导航栏之上,位于两者之间。(多图,建议点击图片放大,会看得更清楚一点)
1)第一种处理方式:将两个Bar同时设为透明可见;
@Override
protected void onStart() {
hideStatusBarNavigationBar();
// hideBottomUIMenu();
super.onStart();
}
/**
* 状态栏、导航栏设为透明 这时导航栏在界面的最上层,意味着可能会盖在一些交互控件之上 这个是有问题的
* 所以最优解有两个:①开启应用时隐藏导航栏,需要时拉出;
* ②开启应用时将导航栏设为透明,同时保留其层级不再UI层级之上
*
* SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION-表示会让应用的主体内容占用系统导航栏的空间 导航栏上浮于视图之上
* SYSTEM_UI_FLAG_HIDE_NAVIGATION-表示隐藏导航栏,但只是表面上隐藏,用手触摸一下屏幕,导航栏就会出来 效果不好
*
* SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN-视图延伸至状态栏区域,状态栏上浮于视图之上
* SYSTEM_UI_FLAG_FULLSCREEN-状态栏隐藏,效果同设置WindowManager.LayoutParams.FLAG_FULLSCREEN
*
* SYSTEM_UI_FLAG_LAYOUT_STABLE-保持整个View稳定, 常和控制System UI悬浮、隐藏的Flags共用, 使View不会因为System UI的变化而重新layout
*/
private void hideStatusBarNavigationBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
window.getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
window.setNavigationBarColor(Color.TRANSPARENT);
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
}
}
效果如下:可以看到,两个bar都已经变为透明可见状态,色块顶到两极。
可以看到,表面上两个bar都设为了透明,OK了,但实际上,我们在使用
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
两个属性的时候,已经将导航栏和状态栏的层级改变了,它们现在位于整个界面的最上层,意味着两者会覆盖住其下面的布局,如果是一些展示效果还好,没有影响,但如果下面是交互控件呢?
如上图,我们在应用界面的最底部,放入两个按钮,此时两个按钮的位置就刚好在导航栏的下面,这时两个按钮都是无法点击状态,这种体验无疑是不好的,怎么改进呢?—— 在界面根布局中加入属性
android:fitsSystemWindows="true"
该属性不设置时默认为false,设置为true之后,会触发View的padding属性来给系统窗口留出空间,怎么理解?
“绝大多数情况下,你都不需要理会status bar或者navigation bar 下面的空间,不过你需要注意不能让你的交互控件(比如Button)藏在status bar 或者 navigation bar下面。而android:fitsSystemWindows="true"的默认行为正好解决了这种情况,这个属性的作用就是通过设置View的padding,使得应用的content部分——Activity中setContentView()中传入的就是content——不会与System windows重叠。System windows 指的就是屏幕上status bar、 navigation bar等系统控件所占据的部分。”
根布局增加android:fitsSystemWindows="true"后,效果如下:
2)第二种处理方式:可以动态地控制两个bar的颜色;
第二种方式建构在第一种处理的基础之上,我们改写之前的hideStatusBarNavgationBar方法:
private void hideStatusBarNavigationBarCopy() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
window.getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.parseColor("#48D1CC"));
window.setNavigationBarColor(Color.parseColor("#48D1CC"));
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
}
}
可以看到,我们为状态栏和导航栏设置了颜色,不再是之前的透明状态,然后分别写入两个点击事件中做判断,效果如下:
录屏软件没有把状态栏录下来,其实变化是一样的。
3)第三种处理:开启应用时,隐藏两个bar,手指沿手机底部或顶部滑动时唤出两个bar;
@Override
protected void onStart() {
// hideStatusBarNavigationBar();
hideBottomUIMenu();
super.onStart();
}
/**
* 隐藏导航栏 状态栏
* SYSTEM_UI_FLAG_IMMERSIVE -沉浸模式, 隐藏状态栏和导航栏并且在状态栏区域滑动可以呼出状态栏
* (这样会系统会清楚之前设置的View.SYSTEM_UI_FLAG_FULLSCREEN或View.SYSTEM_UI_FLAG_HIDE_NAVIGATION标志)
* SYSTEM_UI_FLAG_IMMERSIVE_STICKY- 与上面唯一的区别是, 呼出隐藏的状态栏后不会清除之前设置的
* SYSTEM_UI_FLAG_HIDE_NAVIGATION SYSTEM_UI_FLAG_FULLSCREEN同之前的说明
*/
protected void hideBottomUIMenu() {
if (Build.VERSION.SDK_INT > 11 && Build.VERSION.SDK_INT < 19) {
View v = getWindow().getDecorView();
v.setSystemUiVisibility(View.GONE);
} else if (Build.VERSION.SDK_INT >= 19) {
View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
);
}
}
效果如下:可以看到效果为刚进入应用时隐藏,手指沿底部上滑时唤出导航栏。
4)第四种处理方式:真正沉浸式;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
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);
}
}
}
转自郭神《Android状态栏微技巧,带你真正理解沉浸式模式》效果和(3)中的例子一样。
可以看到之前的三个例子,对于两个bar的处理方法都是在onStart()方法中调用,而郭神的方法是在onWindowFocusChanged()中调用,两者区别在哪呢?
“Activity生命周期中,onStart, onResume, onCreate都不是真正visible的时间点,真正的visible时间点是onWindowFocusChanged()函数被执行时。 如果你想要做一个Activity一加载完毕,就触发什么的话 完全可以用这个”
以上测试机为 小米MIX2S,调为经典导航键 基于Android9.0,如果有不同实现或者其他问题,欢迎留言!
————————————————————————————————————————————————————
2019年快来了,提前祝一波:读者们新年快乐!