本文转载自微信公众号「Android开发编程」,作者Android开发编程。转载本文请联系Android开发编程公众号。
沉浸式就是要给用户提供完全沉浸的体验,使用户有一种置身于虚拟世界之中的感觉;
这种体验在各类游戏中被广泛应用,绝大部分的游戏都会在打开后,使得屏幕被完全被游戏占据,让玩家沉浸其中,从体验上沉浸式效果会更好一些;
网上很多人都写过沉浸式原理的文章,讲解的都不是很清晰,让人很费解;
今天我们就来彻底的总结下沉浸式实现和原理;
从Android4.4 到现在(Android 7.1),关于沉浸式大概可以分成三个阶段:
- /**
- * Window flag: request a translucent status bar with minimal system-provided
- * background protection.
- *
- *
This flag can be controlled in your theme through the
- * {@link android.R.attr#windowTranslucentStatus} attribute; this attribute
- * is automatically set for you in the standard translucent decor themes
- * such as
- * {@link android.R.style#Theme_Holo_NoActionBar_TranslucentDecor},
- * {@link android.R.style#Theme_Holo_Light_NoActionBar_TranslucentDecor},
- * {@link android.R.style#Theme_DeviceDefault_NoActionBar_TranslucentDecor}, and
- * {@link android.R.style#Theme_DeviceDefault_Light_NoActionBar_TranslucentDecor}.
- *
- *
When this flag is enabled for a window, it automatically sets
- * the system UI visibility flags {@link View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and
- * {@link View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}.
- */
- public static final int FLAG_TRANSLUCENT_STATUS = 0x04000000;
设置状态栏透明,并且变为全屏模式。当这个属性有效的时候,会自动设置 system ui visibility的标志SYSTEM_UI_FLAG_LAYOUT_STABLE和SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 。
通过 FLAG_TRANSLUCENT_STATUS 设置状态栏为透明并且为全屏模式,然后通过添加一个与 StatusBar 一样大小的 View,将 View 的 backgroud 设置为我们想要的颜色,从而实现沉浸式。
①, 设置 FLAG_TRANSLUCENT_STATUS,可以在代码中设置,如下:
- activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
或者可以在 theme 设置属性 windowTranslucentStatus,如下:
②.根据有需要,设置一个和 StatusBar 一样大小的占位 View,如果不设置则内容 View 会向上顶一个 StattusBar 的高度。
图片延伸到状态栏只需要设置FLAG_TRANSLUCENT_STATUS就可以
添加占位View的代码如下:
- //获取decorView
- ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
- int count = decorView.getChildCount();
- //判断是否已经添加了statusBarView
- if (count > 0 && decorView.getChildAt(count - 1) instanceof StatusBarView) {
- decorView.getChildAt(count - 1).setBackgroundColor(calculateStatusColor(color, statusBarAlpha));
- } else {
- //新建一个和状态栏高宽的view
- StatusBarView statusView = createStatusBarView(activity, color, statusBarAlpha);
- decorView.addView(statusView);
- }
- ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
- //rootview不会为状态栏留出状态栏空间
- ViewCompat.setFitsSystemWindows(rootView,true);
- rootView.setClipToPadding(true);
- private static StatusBarView createStatusBarView(Activity activity, int color, int alpha) {
- // 绘制一个和状态栏一样高的矩形
- StatusBarView statusBarView = new StatusBarView(activity);
- LinearLayout.LayoutParams params =
- new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity));
- statusBarView.setLayoutParams(params);
- statusBarView.setBackgroundColor(calculateStatusColor(color, alpha));
- return statusBarView;
- }
Android 5.0 是一个里程碑式的版本,google 加入了一个比较重要的方法 setStatusBarColor (对应属性:android:statusBarColor), 通过这个方法,可以很轻松地实现沉浸式状态栏。方法如下:
- /**
- * Sets the color of the status bar to {@code color}.
- *
- * For this to take effect,
- * the window must be drawing the system bar backgrounds with
- * {@link android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS} and
- * {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_STATUS} must not be set.
- *
- * If {@code color} is not opaque, consider setting
- * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and
- * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}.
- *
- * The transitionName for the view background will be "android:status:background".
- *
- */
- public abstract void setStatusBarColor(@ColorInt int color);
不过,要想这个方法生效,必须还要配合一个 Flag 一起使用,必须设置 FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,并且不能设置 FLAG_TRANSLUCENT_STATUS (Android 4.4 才用这个)。
设置了 FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS 表明 Window 负责系统 bar 的 background 绘制,绘制透明背景的系统 bar(状态栏和导航栏),然后用 getStatusBarColor() 和 getNavigationBarColor() 的颜色填充相应的区域,实现代码如下:
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
- //注意要清除 FLAG_TRANSLUCENT_STATUS flag
- getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
- getWindow().setStatusBarColor(getResources().getColor(android.R.color.holo_red_light));
也可以直接在 Theme 中使用,在 vlues-v21 文件夹下添加如下主题:
如果要让图片延申至状态栏,只需设置 windowTranslucentStatus,将 statusBarColor 设置为透明,同时设置 DecorView 的 属性:
- getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN |
- View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
其实Android6.0以上的实现方式和Android 5.0 +是一样,为什么要将它归为一个单独重要的阶段呢?是因为从Android 6.0(API 23)开始,我们可以改状态栏的绘制模式,可以显示白色或浅黑色的内容和图标;
使用Android6.0 以上版本沉浸式的时候会遇到一个问题,那就是 Android 系统状态栏的字色和图标颜色为白色,当状态栏颜色接近浅色的时候,状态栏上的内容就看不清了;
Android 6.0 新添加了一个属性来解决这个问题,属性是 SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,可以设置状态栏字色和图标浅黑色。
- /**
- * Flag for {@link #setSystemUiVisibility(int)}: Requests the status bar to draw in a mode that
- * is compatible with light status bar backgrounds.
- *
- *
For this to take effect, the window must request
- * {@link android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
- * FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS} but not
- * {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_STATUS
- * FLAG_TRANSLUCENT_STATUS}.
- *
- * @see android.R.attr#windowLightStatusBar
- */
- public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 0x00002000;
不过要想这个属性生效的前提是要先设置了FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag ,同时清除了FLAG_TRANSLUCENT_STATUS flag 才会生效。
(1)状态栏字体白色
- getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);//字体默认白色
- getWindow().setStatusBarColor(android.R.color.transparent);//透明背景
(2)状态栏字体黑色
- getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);//黑色字体
- getWindow().setStatusBarColor(android.R.color.transparent);//透明背景
①. View.SYSTEM_UI_FLAG_FULLSCREEN:Activity全屏显示,且状态栏被隐藏覆盖掉
②.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN:Activity全屏显示,但状态栏不会被隐藏覆盖,状态栏依然可见,
③. View.SYSTEM_UI_FLAG_LAYOUT_STABLE
使用了SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN和SYSTEM_UI_FLAG_LAYOUT_STABLE,注意两个Flag必须要结合在一起使用,表示会让应用的主体内容占用系统状态栏的空间
④. View.SYSTEM_UI_FLAG_HIDE_NAVIGATION:隐藏虚拟按键(导航栏)。有些手机会用虚拟按键来代替物理按键。
⑤. View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION:隐藏导航栏 效果同View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
⑥. 有的手机默认全屏显示,有时需要强制不显示全屏就用以下flag
不全屏显示
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
全屏显示
- getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
- /***
- * 状态栏字体适配方案
- * @param activity
- * @param dark
- */
- public static void darkMode(Activity activity, boolean dark) {
- try {
- if (isFlyme4Later()) {
- //魅族
- darkModeForFlyme4(activity.getWindow(), dark);
- } else if (isMIUI6Later()) {
- //小米
- darkModeForMIUI6(activity.getWindow(), dark);
- } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- //其他通用方案
- darkModeForM(activity.getWindow(), dark);
- }
- } catch (Exception e) {
- }
- }
- /***
- * 状态栏字体适配方案
- * @param activity
- * @param dark
- */
- public static void darkMode(Activity activity, boolean dark) {
- try {
- if (isFlyme4Later()) {
- //魅族
- darkModeForFlyme4(activity.getWindow(), dark);
- } else if (isMIUI6Later()) {
- //小米
- darkModeForMIUI6(activity.getWindow(), dark);
- } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- //其他通用方案
- darkModeForM(activity.getWindow(), dark);
- }
- } catch (Exception e) {
- }
- }
- /**
- * 判断是否Flyme4以上
- */
- public static boolean isFlyme4Later() {
- return Build.FINGERPRINT.contains("Flyme_OS_4")
- || Build.VERSION.INCREMENTAL.contains("Flyme_OS_4")
- || Pattern.compile("Flyme OS [4|5]", Pattern.CASE_INSENSITIVE).matcher(Build.DISPLAY).find();
- }
- /**
- * 判断是否为MIUI6以上
- */
- @SuppressLint("PrivateApi")
- public static boolean isMIUI6Later() {
- try {
- Class> clz = Class.forName("android.os.SystemProperties");
- Method mtd = clz.getMethod("get", String.class);
- String val = (String) mtd.invoke(null, "ro.miui.ui.version.name");
- assert val != null;
- val = val.replaceAll("[vV]", "");
- int version = Integer.parseInt(val);
- return version >= 6;
- } catch (Exception e) {
- return false;
- }
- }
- /**
- * android 6.0设置字体颜色
- */
- @RequiresApi(Build.VERSION_CODES.M)
- private static void darkModeForM(Window window, boolean dark) {
- window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
- window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
- window.setStatusBarColor(Color.TRANSPARENT);
- int systemUiVisibility = window.getDecorView().getSystemUiVisibility();
- if (dark) {
- systemUiVisibility |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
- } else {
- systemUiVisibility &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
- }
- window.getDecorView().setSystemUiVisibility(systemUiVisibility);
- }
我们只需要把内容延伸至状态栏和导航栏,然后给根布局设置图片背景,若需要内容不出现在状态栏和导航栏区域则再添加android:fitsSystemWindows="true"既可
- /**
- * 获取状态栏高度
- */
- public static int getStatusBarHeight(Context context) {
- int result = 24;
- int resId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
- if (resId > 0) {
- result = context.getResources().getDimensionPixelSize(resId);
- } else {
- result = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- result, Resources.getSystem().getDisplayMetrics());
- }
- return result;
- }
其实网上有很多成熟的沉浸式方案,我们也没有必要封装,主要是要了解其中的知识点,遇到问题好排查问题
网上的轮子StatusBarUtil
有以下的功能:
- StatusBarUtil.setColor(Activity activity, int color)
设置状态栏半透明
设置状态栏全透明
- StatusBarUtil.setTransparent(Activity activity)
- StatusBarUtil.setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, int color)
- StatusBarUtil.setTranslucentForImageView(Activity activity, int statusBarAlpha, View needOffsetView)
这次知识点总结,希望可以给还没有使用沉浸式的同学一些帮助。如果你已经使用过沉浸式状态栏,可以对各个版本实现的原理有一个更深的了解。
本文题目:Android进阶之沉浸式状态栏原理和使用详解
浏览路径:http://www.csdahua.cn/qtweb/news31/334481.html
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网