成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Android進階之沉浸式狀態欄原理和使用詳解

移動開發 Android
Android系統4.4之前狀態欄一直是黑色的,在4.4中帶來了 windowTranslucentStatus 這一特性,開始引出“沉浸式狀態欄”這個概念。Google 在 Android 4.4 的 API 描述頁面里提到了“Translucent system UI styling”,即透明化的系統UI風格。

[[416377]]

本文轉載自微信公眾號「Android開發編程」,作者Android開發編程。轉載本文請聯系Android開發編程公眾號。

前言

沉浸式就是要給用戶提供完全沉浸的體驗,使用戶有一種置身于虛擬世界之中的感覺;

這種體驗在各類游戲中被廣泛應用,絕大部分的游戲都會在打開后,使得屏幕被完全被游戲占據,讓玩家沉浸其中,從體驗上沉浸式效果會更好一些;

網上很多人都寫過沉浸式原理的文章,講解的都不是很清晰,讓人很費解;

今天我們就來徹底的總結下沉浸式實現和原理;

一、沉浸式概念和為何要用沉浸式

1、沉浸式概念

  • Android系統4.4之前狀態欄一直是黑色的,在4.4中帶來了 windowTranslucentStatus 這一特性,開始引出“沉浸式狀態欄”這個概念。Google 在 Android 4.4 的 API 描述頁面里提到了“Translucent system UI styling”,即透明化的系統UI風格。
  • “沉浸式狀態欄”準確來說應該是“透明欄”,是 4.4 新定義的設計規范;
  • 簡單來說就是在軟件打開的時候通知欄和軟件頂部顏色融為一體,這樣可以使軟件和系統本身更加符合,同時通知欄的顏色不再是白色、黑色簡單的兩種了;
  • 沉浸式表示全屏顯示手機屏幕是沒有手機里面自帶的任何控件;

2、為何要用沉浸式

  • 如果App里面目前都沒有做沉浸式狀態欄,會導致狀態欄呈黑色條狀,而且下面這個的黑色(白色)條狀與App主界面有很明顯的區別。這樣在一定程度上犧牲了視覺高度,界面面積變小,最主要的是用戶的視覺和體驗;
  • 說白了,用戶體驗好,用的爽,留存就高,那么領導肯定讓開發沉浸式主題樣式;

二、沉浸式原理和兼容

從Android4.4 到現在(Android 7.1),關于沉浸式大概可以分成三個階段:

  • Android4.4(API 19) - Android 5.0(API 21):這個階段可以實現沉浸式,但是表現得還不是很好,實現方式為: 通過FLAG_TRANSLUCENT_STATUS設置狀態欄為透明并且為全屏模式,然后通過添加一個與StatusBar 一樣大小的View,將View 的 background 設置為我們想要的顏色,從而來實現沉浸式;
  • Android 5.0(API 21)以上版本:在Android 5.0的時候,加入了一個重要的屬性和方法 android:statusBarColor (對應方法為 setStatusBarColor),通過這個方法我們就可以輕松實現沉浸式。也就是說,從Android5.0開始,系統才真正的支持沉浸式;
  • Android 6.0(API 23)以上版本:其實Android6.0以上的實現方式和Android 5.0 +是一樣,為什么要將它歸為一個單獨重要的階段呢?是因為從Android 6.0(API 23)開始,我們可以改狀態欄的繪制模式,可以顯示白色或淺黑色的內容和圖標(除了魅族手機,魅族自家有做源碼更改,6.0以下就能實現);

1、Android4.4(API 19)- Android 5.0(API 21)

Android在4.4新增了一個重要的屬性:FLAG_TRANSLUCENT_STATUS

  1. /** 
  2.   * Window flag: request a translucent status bar with minimal system-provided 
  3.   * background protection. 
  4.   * 
  5.   * <p>This flag can be controlled in your theme through the 
  6.   * {@link android.R.attr#windowTranslucentStatus} attribute; this attribute 
  7.   * is automatically set for you in the standard translucent decor themes 
  8.   * such as 
  9.   * {@link android.R.style#Theme_Holo_NoActionBar_TranslucentDecor}, 
  10.   * {@link android.R.style#Theme_Holo_Light_NoActionBar_TranslucentDecor}, 
  11.   * {@link android.R.style#Theme_DeviceDefault_NoActionBar_TranslucentDecor}, and 
  12.   * {@link android.R.style#Theme_DeviceDefault_Light_NoActionBar_TranslucentDecor}.</p> 
  13.   * 
  14.   * <p>When this flag is enabled for a window, it automatically sets 
  15.   * the system UI visibility flags {@link View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and 
  16.   * {@link View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}.</p> 
  17.   */ 
  18.  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,可以在代碼中設置,如下:

  1. activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 

或者可以在 theme 設置屬性 windowTranslucentStatus,如下:

②.根據有需要,設置一個和 StatusBar 一樣大小的占位 View,如果不設置則內容 View 會向上頂一個 StattusBar 的高度。

圖片延伸到狀態欄只需要設置FLAG_TRANSLUCENT_STATUS就可以

添加占位View的代碼如下:

  1. //獲取decorView 
  2.         ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); 
  3.         int count = decorView.getChildCount(); 
  4.         //判斷是否已經添加了statusBarView 
  5.         if (count > 0 && decorView.getChildAt(count - 1) instanceof StatusBarView) { 
  6.             decorView.getChildAt(count - 1).setBackgroundColor(calculateStatusColor(color, statusBarAlpha)); 
  7.         } else { 
  8.             //新建一個和狀態欄高寬的view 
  9.             StatusBarView statusView = createStatusBarView(activity, color, statusBarAlpha); 
  10.             decorView.addView(statusView); 
  11.         } 
  12.         ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0); 
  13.         //rootview不會為狀態欄留出狀態欄空間 
  14.         ViewCompat.setFitsSystemWindows(rootView,true); 
  15.         rootView.setClipToPadding(true); 
  16. private static StatusBarView createStatusBarView(Activity activity, int color, int alpha) { 
  17.         // 繪制一個和狀態欄一樣高的矩形 
  18.         StatusBarView statusBarView = new StatusBarView(activity); 
  19.         LinearLayout.LayoutParams params = 
  20.                 new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity)); 
  21.         statusBarView.setLayoutParams(params); 
  22.         statusBarView.setBackgroundColor(calculateStatusColor(color, alpha)); 
  23.         return statusBarView; 

2、Android 5.0(API 21)以上版本

Android 5.0 是一個里程碑式的版本,google 加入了一個比較重要的方法 setStatusBarColor (對應屬性:android:statusBarColor), 通過這個方法,可以很輕松地實現沉浸式狀態欄。方法如下:

  1. /** 
  2.      * Sets the color of the status bar to {@code color}. 
  3.      * 
  4.      * For this to take effect, 
  5.      * the window must be drawing the system bar backgrounds with 
  6.      * {@link android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS} and 
  7.      * {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_STATUS} must not be set
  8.      * 
  9.      * If {@code color} is not opaque, consider setting 
  10.      * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and 
  11.      * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}. 
  12.      * <p> 
  13.      * The transitionName for the view background will be "android:status:background"
  14.      * </p> 
  15.      */ 
  16.     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() 的顏色填充相應的區域,實現代碼如下:

  1. getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 
  2. //注意要清除 FLAG_TRANSLUCENT_STATUS flag 
  3. getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 
  4. getWindow().setStatusBarColor(getResources().getColor(android.R.color.holo_red_light)); 

也可以直接在 Theme 中使用,在 vlues-v21 文件夾下添加如下主題:

  1. <style name="MDTheme" parent="Theme.Design.Light.NoActionBar"
  2.         <item name="android:windowTranslucentStatus">false</item> 
  3.         <item name="android:windowDrawsSystemBarBackgrounds">true</item> 
  4.         <item name="android:statusBarColor">@android:color/holo_red_light</item> 
  5. </style> 

如果要讓圖片延申至狀態欄,只需設置 windowTranslucentStatus,將 statusBarColor 設置為透明,同時設置 DecorView 的 屬性:

  1. getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN |  
  2. View.SYSTEM_UI_FLAG_LAYOUT_STABLE); 

3、Android 6.0 +

其實Android6.0以上的實現方式和Android 5.0 +是一樣,為什么要將它歸為一個單獨重要的階段呢?是因為從Android 6.0(API 23)開始,我們可以改狀態欄的繪制模式,可以顯示白色或淺黑色的內容和圖標;

使用Android6.0 以上版本沉浸式的時候會遇到一個問題,那就是 Android 系統狀態欄的字色和圖標顏色為白色,當狀態欄顏色接近淺色的時候,狀態欄上的內容就看不清了;

Android 6.0 新添加了一個屬性來解決這個問題,屬性是 SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,可以設置狀態欄字色和圖標淺黑色。

  1. /** 
  2.      * Flag for {@link #setSystemUiVisibility(int)}: Requests the status bar to draw in a mode that 
  3.      * is compatible with light status bar backgrounds. 
  4.      * 
  5.      * <p>For this to take effect, the window must request 
  6.      * {@link android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS 
  7.      *         FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS} but not 
  8.      * {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_STATUS 
  9.      *         FLAG_TRANSLUCENT_STATUS}. 
  10.      * 
  11.      * @see android.R.attr#windowLightStatusBar 
  12.      */ 
  13.     public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 0x00002000; 

不過要想這個屬性生效的前提是要先設置了FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag ,同時清除了FLAG_TRANSLUCENT_STATUS flag 才會生效。

(1)狀態欄字體白色

  1. getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);//字體默認白色 
  2. getWindow().setStatusBarColor(android.R.color.transparent);//透明背景 

(2)狀態欄字體黑色

  1. getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);//黑色字體 
  2. getWindow().setStatusBarColor(android.R.color.transparent);//透明背景 

三、實際沉浸式開發中的難點分析

1、沉浸式中常用的flag總結

①. 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

不全屏顯示

  1. getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); 

全屏顯示

  1. getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); 

2、狀態欄字體顏色適配

  1. /*** 
  2.     * 狀態欄字體適配方案 
  3.     * @param activity 
  4.     * @param dark 
  5.     */ 
  6.    public static void darkMode(Activity activity, boolean dark) { 
  7.        try { 
  8.            if (isFlyme4Later()) { 
  9.                //魅族 
  10.                darkModeForFlyme4(activity.getWindow(), dark); 
  11.            } else if (isMIUI6Later()) { 
  12.                //小米 
  13.                darkModeForMIUI6(activity.getWindow(), dark); 
  14.            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 
  15.                //其他通用方案 
  16.                darkModeForM(activity.getWindow(), dark); 
  17.            } 
  18.        } catch (Exception e) { 
  19.        } 
  20.    } 
  21.  /*** 
  22.     * 狀態欄字體適配方案 
  23.     * @param activity 
  24.     * @param dark 
  25.     */ 
  26.    public static void darkMode(Activity activity, boolean dark) { 
  27.        try { 
  28.            if (isFlyme4Later()) { 
  29.                //魅族 
  30.                darkModeForFlyme4(activity.getWindow(), dark); 
  31.            } else if (isMIUI6Later()) { 
  32.                //小米 
  33.                darkModeForMIUI6(activity.getWindow(), dark); 
  34.            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 
  35.                //其他通用方案 
  36.                darkModeForM(activity.getWindow(), dark); 
  37.            } 
  38.        } catch (Exception e) { 
  39.        } 
  40.    } 
  41.   /** 
  42.     * 判斷是否Flyme4以上 
  43.     */ 
  44.    public static boolean isFlyme4Later() { 
  45.        return Build.FINGERPRINT.contains("Flyme_OS_4"
  46.                || Build.VERSION.INCREMENTAL.contains("Flyme_OS_4"
  47.                || Pattern.compile("Flyme OS [4|5]", Pattern.CASE_INSENSITIVE).matcher(Build.DISPLAY).find(); 
  48.    } 
  49.  /** 
  50.     * 判斷是否為MIUI6以上 
  51.     */ 
  52.    @SuppressLint("PrivateApi"
  53.    public static boolean isMIUI6Later() { 
  54.        try { 
  55.            Class<?> clz = Class.forName("android.os.SystemProperties"); 
  56.            Method mtd = clz.getMethod("get", String.class); 
  57.            String val = (String) mtd.invoke(null"ro.miui.ui.version.name"); 
  58.            assert val != null
  59.            val = val.replaceAll("[vV]"""); 
  60.            int version = Integer.parseInt(val); 
  61.            return version >= 6; 
  62.        } catch (Exception e) { 
  63.            return false
  64.        } 
  65.    } 
  66.   /** 
  67.     * android 6.0設置字體顏色 
  68.     */ 
  69.    @RequiresApi(Build.VERSION_CODES.M) 
  70.    private static void darkModeForM(Window window, boolean dark) { 
  71.        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 
  72.        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 
  73.        window.setStatusBarColor(Color.TRANSPARENT); 
  74.        int systemUiVisibility = window.getDecorView().getSystemUiVisibility(); 
  75.        if (dark) { 
  76.            systemUiVisibility |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; 
  77.        } else { 
  78.            systemUiVisibility &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; 
  79.        } 
  80.        window.getDecorView().setSystemUiVisibility(systemUiVisibility); 
  81.    } 

3、fitsSystemWindows理解和用法

  • 在實現沉浸式狀態欄時,我們會用到android:fitsSystemWindows="true"這個屬性;
  • 設置了透明狀態欄(StatusBar)或者導航欄(NavigationBar)之后,activity的內容會延伸至對應的區域,使得該區域出現重疊現象,這對內容包含交互控件的情況影響尤其巨大,為了解決這個情況,fitsSystemWindows屬性出現了,我們可以為任何view添加此屬性,設置了該屬性的view的所有padding屬性將失效,并且系統會根據情況給該view添加paddingTop和paddingBottom(當設置透明狀態欄時,系統會為該view添加一個值等于狀態欄高度的paddingTop,當設置了透明導航欄時,系統會為該view添加一個值等于導航欄高度的paddingBottom);
  • 在默認情況下,多個view設置該屬性時,只有最外層的view才會起作用;我們也可以通過覆寫自定義view的一些方法來決定自身的處理,及子view是否有機會截斷并對fitsSystemWindows做出自己的反應,如DrawerLayout、CoordinatorLayout和CollapsingToolbarLayout就使用了自定義fitsSystemWindow(難怪給drawerLayout設置該屬性時和我們理解的行為不一致)
  • 要實現的效果有以下兩種:背景圖片填滿了整個屏幕、狀態欄和actionBar顏色一致。

我們只需要把內容延伸至狀態欄和導航欄,然后給根布局設置圖片背景,若需要內容不出現在狀態欄和導航欄區域則再添加android:fitsSystemWindows="true"既可

  1. /** 
  2.      * 獲取狀態欄高度 
  3.      */ 
  4.     public static int getStatusBarHeight(Context context) { 
  5.         int result = 24; 
  6.         int resId = context.getResources().getIdentifier("status_bar_height""dimen""android"); 
  7.         if (resId > 0) { 
  8.             result = context.getResources().getDimensionPixelSize(resId); 
  9.         } else { 
  10.             result = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 
  11.                     result, Resources.getSystem().getDisplayMetrics()); 
  12.         } 
  13.         return result; 
  14.     } 

四、沉浸式輪子方案

其實網上有很多成熟的沉浸式方案,我們也沒有必要封裝,主要是要了解其中的知識點,遇到問題好排查問題

網上的輪子StatusBarUtil

有以下的功能:

1、設置狀態欄顏色

  1. StatusBarUtil.setColor(Activity activity, int color) 

設置狀態欄半透明

2、StatusBarUtil.setTranslucent(Activity activity, int statusBarAlpha)

設置狀態欄全透明

  1. StatusBarUtil.setTransparent(Activity activity) 

3、為包含 DrawerLayout 的界面設置狀態欄顏色(也可以設置半透明和全透明)

  1. StatusBarUtil.setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, int color) 

4、為使用 ImageView 作為頭部的界面設置狀態欄透明

  1. StatusBarUtil.setTranslucentForImageView(Activity activity, int statusBarAlpha, View needOffsetView) 

5、在 Fragment 中使用

6、通過傳入 statusBarAlpha 參數,可以改變狀態欄的透明度值,默認值是112。

總結:

 

這次知識點總結,希望可以給還沒有使用沉浸式的同學一些幫助。如果你已經使用過沉浸式狀態欄,可以對各個版本實現的原理有一個更深的了解。

 

責任編輯:武曉燕 來源: Android開發編程
相關推薦

2017-02-17 11:00:57

狀態欄Android

2016-11-29 11:20:08

Android

2017-12-05 12:44:57

Android沉浸式狀態欄APP

2022-11-23 14:47:29

北向開發鴻蒙

2014-06-06 14:03:13

iOS狀態欄提示控件原理

2021-09-07 06:40:25

AndroidLiveData原理

2021-09-06 13:12:05

前端JavaScript編程

2021-09-01 06:48:16

AndroidGlide緩存

2013-07-18 16:09:10

自定義iOS狀態欄iOS開發iOS學習

2021-08-05 20:39:34

AndroidKotlinStandard.kt

2021-08-10 20:41:33

AndroidApp流程

2021-08-17 13:41:11

AndroidView事件

2015-02-12 14:49:36

CGToast狀態欄提示Status

2021-09-02 07:00:01

Glide流程Android

2021-08-25 07:43:17

AndroidSurfaceViewTextureView

2022-02-22 09:16:41

AndroidWindows狀態欄

2021-08-11 17:15:17

AndroidActivity場景

2012-12-24 14:42:48

iOS自定義狀態欄

2013-11-20 15:08:32

iOS開發狀態欄

2021-06-15 15:04:38

Android 12安卓通話
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产欧美一区二区精品久导航 | 国产91精品久久久久久久网曝门 | 日本福利在线观看 | 91综合在线视频 | 日本不卡免费新一二三区 | 亚洲国产电影 | 国产清纯白嫩初高生在线播放视频 | 日本视频免费观看 | 密桃av| 国产精品自拍视频 | 欧美福利 | 黄色片在线观看网址 | 亚洲精品中文字幕在线观看 | 黄色欧美在线 | 国产黄色在线 | 激情欧美一区二区三区中文字幕 | 日韩欧美三级电影 | 精彩视频一区二区三区 | 一区二区三区精品视频 | 精品国产91 | 午夜色婷婷 | 美女视频一区二区 | 国产二区在线播放 | 97人人干| 日韩亚洲视频 | 91极品尤物在线播放国产 | 亚洲国产精品人人爽夜夜爽 | 欧美三级网站 | 中文字幕二区 | 五月婷婷丁香 | 蜜桃av人人夜夜澡人人爽 | 免费一级黄色电影 | 五月激情综合 | 日本免费黄色 | 国产伦精品一区二区三区精品视频 | 午夜影院操 | 日韩欧美电影在线 | 亚洲九九 | 黄色一级电影在线观看 | 日韩久久久久久 | 九九国产在线观看 |