再回首沉浸式狀態列以及魅族手機等系統問題解決
上次寫過一篇解決沉浸式狀態列問題的文章,不過當時僅僅是為了解決兩個問題,沒有過多理解過沉浸式,以至於每次發開需要沉浸式都需要網上搜索半天,然後拷貝程式碼過來,修改修改就得了,今天正好靜下心來好好思考了一下沉浸式。
頁面做成沉浸式的思路有兩個:
第一.就是直接修改狀態列的顏色,需要什麼顏色就將狀態列的顏色改成什麼顏色,就這麼簡單,沒了。純白色沉浸式設定程式碼如下:
/** * 設定沉浸式狀態列 */ public static void immersiveNotificationBar(Activity activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//字型顏色改為黑色,非白色沉浸式狀態列不需要設定 activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); } immersiveNotificationBar(activity, 255); } /** * 設定沉浸式狀態列 */ public static void immersiveNotificationBar(Activity activity, int alpha) { String brand = Build.BRAND; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Window window = activity.getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {//解決華為手機等狀態列上面有一個蒙層問題 try { Class decorViewClazz = Class.forName("com.android.internal.policy.DecorView"); Field field = decorViewClazz.getDeclaredField("mSemiTransparentStatusBarColor"); field.setAccessible(true); field.setInt(window.getDecorView(), Color.TRANSPARENT); //改為透明 } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } } if ("vivo".equalsIgnoreCase(brand) || "OPPO".equals(brand)) {//oppo和vivo手機狀態列最好不要顯示為純白色,官方未給出改變字型顏色為黑色方法 window.setStatusBarColor(Color.argb(alpha, 208, 208, 208)); } else { window.setStatusBarColor(Color.argb(alpha, 255, 255, 255)); } } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); int count = decorView.getChildCount(); if (count > 0 && null != decorView.getChildAt(count - 1)) { decorView.getChildAt(count - 1).setBackgroundColor(calculateStatusColor(Color.WHITE, 30)); } else { View statusView = createStatusBarView(activity, Color.WHITE, 30); decorView.addView(statusView); } setRootView(activity); } } private static View createStatusBarView(Activity activity, @ColorInt int color, int alpha) { View statusBarView = new View(activity); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStarusBarHeight(activity)); statusBarView.setLayoutParams(params); statusBarView.setBackgroundColor(calculateStatusColor(color, alpha)); return statusBarView; } private static void setRootView(Activity activity) { ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0); rootView.setFitsSystemWindows(true); rootView.setClipToPadding(true); } private static int calculateStatusColor(@ColorInt int color, int alpha) { float a = 1 - alpha / 255f; int red = color >> 16 & 0xff; int green = color >> 8 & 0xff; int blue = color & 0xff; red = (int) (red * a + 0.5); green = (int) (green * a + 0.5); blue = (int) (blue * a + 0.5); return 0xff << 24 | red << 16 | green << 8 | blue; } public static int getStarusBarHeight(Context context) { int statusBarHeight1 = -1; int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); if (resourceId > 0) { statusBarHeight1 = context.getResources().getDimensionPixelSize(resourceId); } return statusBarHeight1; }
程式碼複製過去就可以了,然後將setStatusBarColor裡的顏色值改成自己的就可了,很簡單。
第二種方法,就是將狀態列的顏色改為透明,然後頁面設定為全屏顯示,自己頁面頂部預留一個view來當做狀態列。這個呢,靈活性高,可控性也好,相對於第一種來說適用範圍更廣,可以說就是我自己想讓狀態列顯示什麼就顯示什麼,當然了仔細想想其實這是一種假象。具體過程分為三個步驟:
1)頁面設定為全屏且顯示狀態列,狀態列為透明
/** * 通過設定全屏,設定狀態列透明 * * @param activity */ private void fullScreen(Activity activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { //5.x開始需要把顏色設定透明,否則導航欄會呈現系統預設的淺灰色 Window window = activity.getWindow(); View decorView = window.getDecorView(); //兩個 flag 要結合使用,表示讓應用的主體內容佔用系統狀態列的空間 int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; } window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); decorView.setSystemUiVisibility(option); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(Color.TRANSPARENT); //導航欄顏色也可以正常設定 // window.setNavigationBarColor(Color.TRANSPARENT); } else { Window window = activity.getWindow(); WindowManager.LayoutParams attributes = window.getAttributes(); int flagTranslucentStatus = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; int flagTranslucentNavigation = WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; attributes.flags |= flagTranslucentStatus; // attributes.flags |= flagTranslucentNavigation; window.setAttributes(attributes); } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {//解決華為手機等狀態列上面有一個蒙層問題 try { Class decorViewClazz = Class.forName("com.android.internal.policy.DecorView"); Field field = decorViewClazz.getDeclaredField("mSemiTransparentStatusBarColor"); field.setAccessible(true); field.setInt(getWindow().getDecorView(), Color.TRANSPARENT); //改為透明 } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } } }
2)設定佈局中的頂部view高度為狀態列高度(或者自己想著頂部直接弄成頂天的圖片也可以)
3)設定頂部view的顏色,隨意修改達到沉浸式,甚至可以新增圖片都無所謂
好了,沉浸式狀態列問題就這樣基本解決了,再回頭想想,真的沒什麼。不過最讓人頭疼的應該是國產系統的適配問題解決,沉浸式中最難的應該就是屬於白色沉浸式問題,狀態列顏色變成深色問題。下面我們來解決魅族手機狀態列字型顏色問題。
問題如下:
Activity+四個Fragment,Fragment切換時切換狀態列顏色沒問題,但是在其中一個Fragment中跳轉另一個Activity,然後再回來的時候設定黑色狀態列的那個Fragment就失效了,請問是怎麼回事?每次點選切換Fragment的時候都有設定狀態列顏色。
很不幸我也遇到了這個問題,我們分析分析為什麼?魅族手機牛逼的地方在於他會自動讀取狀態列下面view的顏色值,然後根據讀取到的顏色值來自動設定字型的顏色,尼瑪,這就完全不可控了啊!!!!我覺得應該不止魅族系統這樣,奧,對了,使用魅族官網的設定狀態列字型顏色的程式碼也照樣沒卵用。那麼我們怎麼辦?欺騙系統,我們可以欺騙系統,怎麼欺騙?就是當系統讀取狀態列後面view的時候,我們提前手動設定view的顏色,等著讀取完了我們再改成我們想要的顏色,這不就解決了!還是以需要狀態列字型為黑色為例(沉浸式狀態列設定方式為上面第二種方法):
第一步:直接操作我們要顯示的沉浸式view或者在view上面再覆蓋一層也可以,我們就以上面再覆蓋一層view為例吧,xml中view的顏色要設定為白色。
重寫window獲取焦點的方法,當頁面獲取到焦點時我們將覆蓋的view設定為透明顏色
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
if (null == mView) {//mView就是狀態列或者上面覆蓋的一層
mView = findViewById(R.id.status_view);
mView.post(new Runnable() {//為了解決初次進來狀態列字型顏色為白色
@Override
public void run() {
mView.setBackgroundColor(Color.TRANSPARENT);
}
});
} else {
mView.setBackgroundColor(Color.TRANSPARENT);
}
}
}
第二步:就是當頁面徹底不可見的時候我們再次將view的北京顏色設定為白色,當頁面再次可見時扔會執行上面程式碼設定為透明
@Override
protected void onStop() {
super.onStop();
if (null != mView) {
mView.setBackgroundColor(Color.WHITE);
}
}
這裡需要說明為什麼是在onstop中而不是onpause方法中設定,在onpause中設定會讓使用者看見白色的狀態列,所以用在onstop中設定。
最後貼一張圖(魅藍note6)手機上的效果: