温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

透明系统栏及沉浸模式的总结

发布时间:2020-09-01 18:41:04 来源:网络 阅读:3320 作者:一剑围城 栏目:移动开发

关于所谓的“沉浸式”,我有许多话要说,因为这个东西实在是折磨了我许多的时间。实现的方式有许多,兼容性问题也不少。官方文档也让我感到也有些云里雾里。那些“长得很相似”的Flag,适用情况很接近的设置方法,让我不得不一个个测试,然而却一次次推翻。模拟器上测试;真机上测试;4.4版本上的测试;5.0后版本的测试;有导航栏手机上的测试;老掉牙手机上的测试。总而言之,这个东西的探索让我深刻地体会到android系统兼容性问题的麻烦。

当你功能开发占据10%,而处理兼容性问题占据90%的时候。你不得不思考这样一个问题:“兼容性真的有那么重要吗?”撇开这个问题,以投入和产出的角度显然是不太划得来的,但是收获的角度:我学习了解了View的加载机制、DecorView在不同版本上的实现以及发展、养成了遇到问题第一时间查阅官方文档的习惯。

 

关键词

1、系统栏Systembar(包括状态栏Statusbar,导航栏Navigationbar)

2、内容主体暂且可以理解为Window中除了Systembar以外的窗口

 

实现原理:

1、将SystemBar透明化

2、根据不同情况决定内容主体是否需要延伸到SystemBar下方

 

一、将SystemBar透明化

主题中设置属性:

<itemname="android:windowTranslucentStatus">true</item>

<itemname="android:windowTranslucentNavigation">true</item>


代码中设置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下

主题中设置属性:

      <itemname="android:windowFullscreen">true</item>

代码中设置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系统提供了一些透明状态栏/导航栏的主题以供使用:

<stylename="Theme.Material.Light.NoActionBar.TranslucentDecor">

    <item name="windowTranslucentStatus">true</item>

    <itemname="windowTranslucentNavigation">true</item>

    <itemname="windowContentOverlay">@null</item>

</style>

 

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及以上

同方法一:

<itemname="android:windowTranslucentStatus">true</item>

      <itemname="android:windowTranslucentNavigation">true</item>

结合android:fitsSystemWindows属性

 

方法三:适合4.0及以下的版本

<itemname="android:windowFullscreen">true</item>

      <itemname="android:windowContentOverlay">@null</item>

或者代码中:

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


向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI