透明系统栏及沉浸模式的总结
关于所谓的“沉浸式”,我有许多话要说,因为这个东西实在是折磨了我许多的时间。实现的方式有许多,兼容性问题也不少。官方文档也让我感到也有些云里雾里。那些“长得很相似”的Flag,适用情况很接近的设置方法,让我不得不一个个测试,然而却一次次推翻。模拟器上测试;真机上测试;4.4版本上的测试;5.0后版本的测试;有导航栏手机上的测试;老掉牙手机上的测试。总而言之,这个东西的探索让我深刻地体会到android系统兼容性问题的麻烦。
创新互联建站主营淇县网站建设的网络公司,主营网站建设方案,成都app软件开发,淇县h5小程序设计搭建,淇县网站营销推广欢迎淇县等地区企业咨询
当你功能开发占据10%,而处理兼容性问题占据90%的时候。你不得不思考这样一个问题:“兼容性真的有那么重要吗?”撇开这个问题,以投入和产出的角度显然是不太划得来的,但是收获的角度:我学习了解了View的加载机制、DecorView在不同版本上的实现以及发展、养成了遇到问题第一时间查阅官方文档的习惯。
关键词
1、系统栏Systembar(包括状态栏Statusbar,导航栏Navigationbar)
2、内容主体暂且可以理解为Window中除了Systembar以外的窗口
实现原理:
1、将SystemBar透明化
2、根据不同情况决定内容主体是否需要延伸到SystemBar下方
一、将SystemBar透明化
主题中设置属性:
代码中设置Flag:
getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);//透明状态栏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);//透明导航栏
5.0后设置:
getWindow().setStatusBarColor(Color.TRANSPARENT);
getWindow().setNavigationBarColor(Color.TRANSPARENT);
二、根据不同情况决定内容主体是否需要延伸到SystemBar下
主题中设置属性:
代码中设置Flag:
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
FLAG_TRANSLUCENT_STATUS单独使状态栏透明化
FLAG_TRANSLUCENT_NAVIGATION单独使导航栏透明化
4.1后更多选项的设置:
ViewdecorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(Flag);
Flag的值需要了解以下几种情况:
1、SYSTEM_UI_FLAG_FULLSCREEN
2、SYSTEM_UI_FLAG_HIDE_NAVIGATION
3、SYSTEM_UI_FLAG_IMMERSIVE
4、SYSTEM_UI_FLAG_IMMERSIVE_STICKY
5、SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
6、SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
7、SYSTEM_UI_FLAG_LAYOUT_STABLE
8、SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
SYSTEM_UI_FLAG_FULLSCREEN
隐藏状态栏,主体向上偏移,但在手动下拉会重新显示状态栏并清除该flag。
View.SYSTEM_UI_FLAG_FULLSCREEN |View.SYSTEM_UI_FLAG_LAYOUT_STABLE
限制主体位置保持不变,状态栏留白
SYSTEM_UI_FLAG_HIDE_NAVIGATION
隐藏导航栏,主体向下偏移,由于导航栏太重要了,所以只要和Activity进行任何交互都会重新显示导航栏,并清除该flag和SYSTEM_UI_FLAG_FULLSCREEN。
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
限制主体位置保持不变,导航栏留白
SYSTEM_UI_FLAG_LAYOUT_STABLE
保持内容主体位置不变,不随着Systembar的隐藏显示而偏移
SYSTEM_UI_FLAG_IMMERSIVE
沉浸模式:只能和SYSTEM_UI_FLAG_HIDE_NAVIGATION结合使用。简单交互不会重新显示导航栏
SYSTEM_UI_FLAG_IMMERSIVE_STICKY
沉浸模式:只能和SYSTEM_UI_FLAG_FULLSCREEN或SYSTEM_UI_FLAG_HIDE_NAVIGATION结合使用。
下拉状态栏位置可以短暂显示状态栏和导航栏,然后再次隐藏。
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
主体占据全部屏幕,但不隐藏状态栏,这里的layout可以理解为内容主体
SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
主体占据全部屏幕,但不隐藏导航栏,这里的layout可以理解为内容主体
SYSTEM_UI_FLAG_LAYOUT_STABLE
这个Flag确实有点难以理解,唯一读懂的是在各种flag切换的时候能保持内容主体的稳定性。如果你不想flag变换的时候内容主体上下偏移,就需要设置SYSTEM_UI_FLAG_LAYOUT_STABLE。
View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
浅色状态栏模式:对应于图标变深色,防止浅色背景导致状态栏看不清,不设置是深色模式
实践验证过程:
View decorView =getWindow().getDecorView();
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
状态栏不会隐藏,仍占据着位置,但内容可以侵入状态栏下方
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);
View.SYSTEM_UI_FLAG_FULLSCREEN 状态栏下滑,主体布局就会下降一个状态栏的高度,很突兀
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN状态栏下滑,主体布局位置不变化
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
这个和单独使用View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN区别在哪儿,还没发现
andriod系统提供了一些透明状态栏/导航栏的主题以供使用:
SYSTEM_UI_FLAG_HIDE_NAVIGATION |SYSTEM_UI_FLAG_IMMERSIVE
SYSTEM_UI_FLAG_IMMERSIVE必须与SYSTEM_UI_FLAG_HIDE_NAVIGATION 结合使用才有意义
加上SYSTEM_UI_FLAG_IMMERSIVE,则导航栏在隐藏后不会因与界面的交互而呼出。
类似于看视频时进入全屏模式。
SYSTEM_UI_FLAG_FULLSCREEN |SYSTEM_UI_FLAG_HIDE_NAVIGATION
进入全屏模式,但用户与界面进行最简单的交互都会重新显示状态栏,导航栏
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION|View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE
进入全屏模式,用户交互会重新显示状态栏,导航栏,不会再隐藏
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION| View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
进入全屏模式,用户交互会短暂显示状态栏,导航栏,然后再隐藏
总结:
比较简洁的透明状态栏的做法是:
方法一:适合4.1及以上
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP){
View window =getWindow().getDecorView();
window.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
getWindow().setStatusBarColor(Color.TRANSPARENT);
}
对于不需要延伸至状态栏下方的组件,使用android:fitsSystemWindows="true"将进行设置即可。本质上是添加一个状态栏高度的paddingTop属性,所以android:layout_height属性不要使用"250dp"这种固定数值,可以使用"wrap_content"结合android:minHeight="250dp"的方式。
方法二:适合4.1及以上
同方法一:
结合android:fitsSystemWindows属性
方法三:适合4.0及以下的版本
或者代码中:
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
方法三使用android:fitsSystemWindows是无效的。所以我的做法是给需要向下偏移的组件设置一个paddingTop的值,并在v21,v19等不同版本设置不同的值做兼容(源码可知statusbar高度是25dp)。这里默认所有手机平台都是25dp。
网上的做法是选择性填充一个View,该View的Height是动态获取的statusbar的高度。这个做法兼容了不同厂商自己定制的UI使用不同于Google官方25dp的状态栏高度的情况。如红米note2是20dp,魅族note5是22dp。
另外对于setFlags方法,同样有单独设置状态栏和导航栏的方法:
getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
对于这两个属性而言,android:fitsSystemWindows有效,但在设置了FLAG_TRANSLUCENT_NAVIGATION的时候,内容主体也会向下偏移一个导航栏的高度,可见这里的android:fitsSystemWindows指的是SystemBar,而不仅仅是StatusBar。
方法四:适合于5.0以后
结合Design Support库中的CoordinatorLayout+AppBarLayout+CollapsingToolbarLayout,并且结合使用fitsSystemWindows。这也是我第一个接触的方法。需要注意的是fitsSystemWindows属性在CoordinatorLayout中有不同的实现,fitsSystemWindows=“true”反而表示是否出现在状态栏下方。知道真相的我眼泪掉下来。
方法在Fragment中会有问题(例如在FragmentTabhost中使用),使用replace切换则正常,使用add切换的Fragment则fitSystemWindow失效,必须调用onCreate才能生效,我滴个天~~~~最后我只能放弃fitSystemWindow,使用方法三中的设置paddingTop的方法。
方法五:使用第三方框架
SystemBarTint https://github.com/jgilfelt/SystemBarTint
StatusBarUtil https://github.com/laobie/StatusBarUtil
备注:
使用Theme的方式相比代码中设置的优势:
1、更易于维护,并且不易出错
2、更顺畅的UI过渡,因为系统在初始化主Activity之前就已经知道了渲染UI所需要的相关信息
参考资料:
Managing the System UI
https://developer.android.com/training/system-ui/index.html
SystemBarTint
https://github.com/jgilfelt/SystemBarTint
StatusBarUtil
https://github.com/laobie/StatusBarUtil
网站栏目:透明系统栏及沉浸模式的总结
路径分享:http://pwwzsj.com/article/pepcgh.html