--- by Blue.Wu
背景:近期笔者在某款app时,需求要对导航栏(返回,home键,recent键的那部分)进行隐藏,此外还要做相关操作的屏蔽避免导航栏恢复显示。原本的方案是通过发广播给system ui,在system ui做remove导航栏的操作实现。后来架构设计阶段评估该方案对于外部模块的耦合太强,因此希望对方案进行优化,要求只在app自身做改动就可以实现。经过一番折腾完成之后,特将一些心得整理出来供参考。
心得1. 如何在不拉伸主要显示区域情况下,隐藏导航栏和状态栏。
笔者原本采用如下代码实现隐藏效果:
View decorView = activity.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);
实践后发现虽然能实现隐藏状态栏和导航栏,但会出现原有显示区域的布局被拉伸的情况。这与我们的要求不相符,详细研究View的setSystemUiVisibility方法可以传入的flag的含义和用法如下:
1. View.SYSTEM_UI_FLAG_VISIBLE:显示状态栏,Activity不全屏显示(恢复到有状态的正常情况)。
2. View.INVISIBLE:隐藏状态栏,同时Activity会伸展全屏显示。
3. View.SYSTEM_UI_FLAG_FULLSCREEN:Activity全屏显示,且状态栏被隐藏覆盖掉。
4. View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN:Activity全屏显示,但状态栏不会被隐藏覆盖,状态栏依然可见,Activity顶端布局部分会被状态遮住。
5. View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION:效果同View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
6. View.SYSTEM_UI_LAYOUT_FLAGS:效果同View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
7. View.SYSTEM_UI_FLAG_HIDE_NAVIGATION:隐藏虚拟按键(导航栏)。有些手机会用虚拟按键来代替物理按键。
8. View.SYSTEM_UI_FLAG_LOW_PROFILE:状态栏显示处于低能显示状态(low profile模式),状态栏上一些图标显示会被隐藏。
9.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY:粘性沉浸模式,向内滑动的操作会让系统栏临时显示,并处于半透明的状态。此时没有标签会被清除,系统UI可见性监听器也不会被触发。如果用户没有进行操作,系统栏会在一段时间内自动隐藏。
10.View.SYSTEM_UI_FLAG_IMMERSIVE:非粘性沉浸模式,它是基于其他设置过的标签(SYSTEM_UI_FLAG_HIDE_NAVIGATION和SYSTEM_UI_FLAG_FULLSCREEN)来隐藏系统栏的。当用户向内滑动,系统栏重新显示并保持可见。
通过上面的分析可以发现,带有LAYOUT的flag,表示Activity会延伸到原本属于状态栏或导航栏的位置,所以导致了布局拉伸;在要求隐藏状态栏的地方,只需要用View.SYSTEM_UI_FLAG_FULLSCREEN即可。这时候,导航栏存在,状态栏隐藏并且不会拉伸。
若要求导航栏和状态栏都隐藏,采用View.SYSTEM_UI_FLAG_FULLSCREEN和 View.SYSTEM_UI_FLAG_HIDE_NAVIGATION配合即可。也可以使用View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY和View.SYSTEM_UI_FLAG_HIDE_NAVIGATION配合,效果是一样的。
心得2. 如何屏蔽触摸手势滑动调出导航栏。
上述的设置还是无法解决点击屏幕导航栏恢复的问题。经过查找资料了解到滑动屏幕主要会被PhoneWindowManager的SystemGesturesPointerEventListener所截获,而且研究发现,笔者当前采用的mtk方案在framework有针对设置了FULL_SCREEN的界面无法禁止状态栏下拉的问题,做了一些扩展,包括在View类中增加了public static final int SYSTEM_UI_FLAG_IMMERSIVE_GESTURE_ISOLATED = 0x00002000; 并在SystemGesturesPointerEventListener的onSwipeFromTop,onSwipeFromBottom等方法里添加isGestureIsolated()方法判断,isGestureIsolated代码如下:
1. private boolean isGestureIsolated() {
2. + WindowState win = mFocusedWindow != null ? mFocusedWindow : mTopFullscreenOpaqueWindowState;
3. + if (win != null && (win.getSystemUiVisibility() & View.SYSTEM_UI_FLAG_IMMERSIVE_GESTURE_ISOLATED) != 0)
4. + return true;
5. + else
6. + return false;
7. + }
因此我们只需要在setSystemUiVisibility中加上SYSTEM_UI_FLAG_IMMERSIVE_GESTURE_ISOLATED就可以屏蔽在屏幕上的滑动了。
注:该方法仅针对MTK手机平台适用,若是高通平台,则需要framework自行扩展类似接口。
心得3. 如何在对话框调起时不显示状态栏。
setSystemUiVisibility隐藏状态栏后,弹出Dialog或输入法键盘时,状态栏又会跑出来.
在请教了资深开发者之后发现原因,如下是framework中Diaolgshow的处理:
public void show() {
......
WindowManager.LayoutParams l = mWindow.getAttributes();
......
mWindowManager.addView(mDecor, l);
......
}
dialog是在窗体addview,并且沿用了原窗体的参数.
因此如果原窗体没有设置full_screen的flag,则addview刷新窗体时,也是非full_screen而把状态栏调出来.
(输入法键盘的显示也是类似情况)
解决方案:如果要隐藏状态栏并且避免dialog弹出时状态栏跑出来,需要改变Window的参数即可:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);