服務項目

需求溝通

navigationbar

  android中沉浸式狀態欄的文章已經滿大街了,可是在實現某些效果時,還是得各種搜索,測試一通后,最后還常常滿足不了要求,即使好不容易在一部手機上滿足了需求,放在另外一手機上,發現效果還各種不適配。今天把自己這幾天學到的關于沉浸式狀態欄知識進行總結下。

  問題

  比如我想實現以下效果:

  同一個Activity需要動態變換標題欄和狀態欄文字字體色值,該如何實現?

  一個Activity包含多個Fragment切換時,不同的Fragment的狀態欄背景,狀態欄文字顏色和圖標要求不一樣怎么實現?

  設置沉浸式狀態欄,各個android版本之間差別如何,那么多flag,長得都一樣,都有什么區別?

  無圖無真相,帶著這幾個問題,先上兩張我實現的效果圖。

  下面是同一個activity切換不同fragment時,狀態欄文字顏色跟著變化的效果圖:

  同一個activity切換不同fragment時,狀態欄文字顏色跟著變化

  下圖是同一個Activity向上滾動時,標題欄和狀態欄文字顏色根據變化的效果:

  Activity向上滾動時,標題欄和狀態欄文字顏色根據變化

  1. 實現透明狀態欄常規方法

  protected boolean useThemestatusBarColor = false;//是否使用特殊的標題欄背景顏色,android5.0以上可以設置狀態欄背景色,如果不使用則使用透明色值 protected boolean useStatusBarColor = true;//是否使用狀態欄文字和圖標為暗色,如果狀態欄采用了白色系,則需要使狀態欄和圖標為暗色,android6.0以上可以設置protected void setStatusBar() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {//5.0及以上 View decorView = getWindow().getDecorView(); int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; decorView.setSystemUiVisibility(option); //根據上面設置是否對狀態欄單獨設置顏色 if (useThemestatusBarColor) { getWindow().setStatusBarColor(getResources().getColor(R.color.colorTheme)); } else { getWindow().setStatusBarColor(Color.TRANSPARENT); } } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {//4.4到5.0 WindowManager.LayoutParams localLayoutParams = getWindow().getAttributes(); localLayoutParams.flags = (WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | localLayoutParams.flags); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !withoutUseStatusBarColor) {//android6.0以后可以對狀態欄文字顏色和圖標進行修改 getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); } }

  在Activity布局的根節點處加上android:fitsSystemWindows=”true”屬性就可以了,要不布局會跑到狀態欄和導航欄下面,與導航欄和狀態欄重疊,這當然不是我們希望的。

  Activity通過上面的設置,可以實現如下效果:

  狀態欄文字顏色和圖標為黑色

  上面設置狀態欄文字顏色和圖標為暗色主要采用了以下兩個標志:

  //設置狀態欄文字顏色及圖標為深色getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);

  View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 是從API 16開始啟用,實現效果:

  視圖延伸至狀態欄區域,狀態欄懸浮于視圖之上

  View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR 是從 API 23開始啟用,實現效果:

  設置狀態欄圖標和狀態欄文字顏色為深色,為適應狀態欄背景為淺色調,該Flag只有在使用了FLAG_DRWS_SYSTEM_BAR_BACKGROUNDS,并且沒有使用FLAG_TRANSLUCENT_STATUS時才有效,即只有在透明狀態欄時才有效。

  2. 同一個Activity包含多個Fragment時,如何實現不同fragment的狀態欄背景和文字顏色不一樣

  如下面的效果圖:

  同一個activity切換不同fragment時,狀態欄文字顏色跟著變化

  就是設置了狀態欄為暗色后,還得設置回來,這其實主要靠下面兩個flag標識,結全上面的兩個flag標識就能實現。

  //設置狀態欄文字顏色及圖標為淺色getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);

  View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 前面說過了,是為了讓視圖能延伸到狀態欄區域,使狀態欄懸浮在視圖布局之上。

  View.SYSTEM_UI_FLAG_LAYOUT_STABLE

  保持整個View穩定, 常和控制System UI懸浮, 隱藏的Flags共用, 使View不會因為System UI的變化而重新layout。

  將上面的代碼放在不同fragment切換處即可實現上面的效果了:

  private void switchTo(int position) { FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); switch (position) { case 0://首頁 hideShowFragment(transaction, fourFragment, thirdFragment, secondFragment, homeFragment);//展示第一個fragmentgetWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); getWindow().getDecorView().findViewById(android.R.id.content).setPadding(0, 0, 0, CommonUtils.navigationHeight); break; case 1: //活動 hideShowFragment(transaction, homeFragment, thirdFragment, fourFragment, secondFragment);//展示第二個fragment if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//實現狀態欄圖標和文字顏色為暗色 getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); } getWindow().getDecorView().findViewById(android.R.id.content).setPadding(0, 0, 0, CommonUtils.navigationHeight); break; case 2: //所有圖片 hideShowFragment(transaction, homeFragment, fourFragment, secondFragment, thirdFragment);//展示第三個fragment if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//實現狀態欄圖標和文字顏色為暗色 getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); } getWindow().getDecorView().findViewById(android.R.id.content).setPadding(0, 0, 0, CommonUtils.navigationHeight); break; case 3://我的 hideShowFragment(transaction, homeFragment, secondFragment, thirdFragment, fourFragment);//展示第四個fragment //實現狀態欄圖標和文字顏色為淺色 getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); getWindow().getDecorView().findViewById(android.R.id.content).setPadding(0, 0, 0, CommonUtils.navigationHeight); break; default: break; } }//fragment切換實現private void hideShowFragment(FragmentTransaction transaction, Fragment fragment1, Fragment fragment2, Fragment fragment3, Fragment fragment4) { transaction.hide(fragment1); transaction.hide(fragment2); transaction.hide(fragment3); transaction.show(fragment4); transaction.commitAllowingStateLoss(); }

  大家可能會注意到,我這里切換每個fragment時,有下面這樣一行代碼:

  getWindow().getDecorView().findViewById(android.R.id.content).setPadding(0, 0, 0, CommonUtils.navigationHeight);

  這行代碼干什么用的,因為我們這里首頁和我的頁面,需要背景圖片填充到狀態欄,故不能使用android:fitsSystemWindows屬性,故在實現上面效果時帶有底部導航欄手機上就會存在一個大坑,解決辦法見第3章節。同時不使用android:fitsSystemWindows屬性,怎么讓布局不遮擋狀態欄文字,解決辦法見第4章節。

  3. 帶有底部導航欄手機底部導航按鈕會和navigationbar重疊

  如下圖所示:

  底部導航欄與底部按鈕重疊

  全屏時,由于視圖布局會填充到狀態欄和導航欄下方,如果不使用android:fitsSystemWindows=”true”屬性,就會使底部導航欄和應用底部按鈕重疊,導視按鈕點擊失效,這該怎么辦?

  經過網上搜索相關資料,其實實現方法和實現透明狀態欄效果方法一致。

  解決的方法:

  先判斷手機是否有物理按鈕判斷是否存在NavigationBar;

  計算底部的NavigationBar高度;

  最后設置視圖邊距。

  3.1 通過反射判斷手機是否有物理按鈕NavigationBar

  //判斷是否存在NavigationBarpublic static boolean checkDeviceHasNavigationBar(Context context) { boolean hasNavigationBar = false; Resources rs = context.getResources(); int id = rs.getIdentifier("config_showNavigationBar", "bool", "android"); if (id > 0) { hasNavigationBar = rs.getBoolean(id); } try { Class systemPropertiesClass = Class.forName("android.os.SystemProperties"); Method m = systemPropertiesClass.getMethod("get", String.class); String navBarOverride = (String) m.invoke(systemPropertiesClass, "qemu.hw.mainkeys"); if ("1".equals(navBarOverride)) { hasNavigationBar = false; } else if ("0".equals(navBarOverride)) { hasNavigationBar = true; } } catch (Exception e) { } return hasNavigationBar;}

  3.2 計算底部的NavigationBar高度

  /** * 獲取底部導航欄高度 * @return */public static int getNavigationBarHeight(Context context) { Resources resources = context.getResources(); int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android"); //獲取NavigationBar的高度 navigationHeight = resources.getDimensionPixelSize(resourceId); return navigationHeight;}

  3.3 設置視圖邊距

  getWindow().getDecorView().findViewById(android.R.id.content).setPadding(0, 0, 0, CommonUtils.navigationHeight);

  通過上面的設置,會使布局距離底部導航欄的高度。

  最后實現效果如下:

  導航欄與底部按鈕不重疊

  參考文章:android 6.0導航欄 NavigationBar影響視圖解決辦法

  4. 不使用fiySystemWindow屬性,布局怎么能不遮擋狀態欄文字

  跟第三章節類似,在主頁中,需要使布局中帶文字的布局向上margin狀態欄的高度。

  4.1 先在布局中設置一個占空LinearLayout

  我們先來看看第三個Fragment的布局實現

  4.2 在代碼中動態設置占空布局高度

  /** * 動態的設置狀態欄 實現沉浸式狀態欄 */private void initState() { //當系統版本為4.4或者4.4以上時可以使用沉浸式狀態欄 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //透明狀態欄 getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); //透明導航欄 getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); bindingView.llBar.setVisibility(View.VISIBLE); //獲取到狀態欄的高度 int statusHeight = CommonUtils.getStatusBarHeight(getActivity()); //動態的設置隱藏布局的高度 LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) bindingView.llBar.getLayoutParams(); params.height = statusHeight; bindingView.llBar.setLayoutParams(params); }}/** * 通過反射的方式獲取狀態欄高度 * * @return */public static int getStatusBarHeight(Context context) { try { Class c = Class.forName("com.android.internal.R$dimen"); Object obj = c.newInstance(); Field field = c.getField("status_bar_height"); int x = Integer.parseInt(field.get(obj).toString()); return context.getResources().getDimensionPixelSize(x); } catch (Exception e) { e.printStackTrace(); } return 0;}

  對于上面的第二個和第三個fragment的實現,為了讓視圖布局不遮擋狀態欄文字,主要是通過先給界面設置占位布局,然后在代碼中動態設置該布局為狀態欄高度,這其實就是讓狀態欄懸浮在這個占空布局上面。視圖布局位于占空布局下方,從而達到視圖布局不遮擋狀態欄效果。

  上面對于版本的判斷,如果android版本大于4.4, 則讓該布局顯示出來,而版本低于4.4, 由于沒有沉浸式狀態欄效果,則不需要給界面設置占空布局。

  而對于第一個首頁和第四個我的fragment,則需要布局的圖片填充到狀態欄底下,而標題欄要位于狀態欄下方,這其實只需要一種取巧實現,一般手機狀態欄高度都是在25dp左右,當然在代碼中動態獲取狀態欄高度,動態設置也可以。我這里是簡單實現,讓標題欄marginTop狀態欄高度即可,對于android不同版本,可以如下設置。

  對于values中dimens.xml設置狀態欄的高度:

  0dp

  對于values-v19中dimens.xml設置狀態欄的高度:

  25dp

  5. 同一個Activity上下滑動動態變換標題欄和狀態欄文字字體色值

  效果如下:

  Activity向上滾動時,標題欄和狀態欄文字顏色根據變化

  這種布局實現主要是依靠CoordinatorLayout+AppBarLayout+CollapsingToolbarLayout+Toolbar+NestedScrollView來實現,之前我也寫過類似的博文來介紹CoordinatorLayout的使用方法。感興趣的小伙伴可以參下:android沉浸式狀態欄、fitsSystemWindows、標題欄折疊

  下面我們說說怎么在界面滑動時,修改狀態欄和標題欄文字顏色。

  這個主要通過監聽AppBarLayout滑動的距離,向上滑動,如果大于標題欄的高度,則要動態改變標題欄文字顏色,當標題欄折疊時,改變狀態欄文字顏色及返回銨鈕圖標,同時狀態欄文字顏色變成暗色。

  向下滑動時,隨著標題欄慢慢消失,需要把狀態欄文字顏色變成淺色調。

  總結

  根據android提供的widnow的flag,狀態欄淺色調和深色調,我們可以實時動態變換一個Activity的狀態欄顏色,同時結合CoordinatorLayout,我們可以實現更加復雜的效果。

  代碼傳送門:

  https://github.com/xiewenfeng/statusbartextcolorchange



       溫馨提示:大連仟源科技有限公司匯聚眾多網絡精英與行業頂尖設計師、程序員,以WEB互動應用、移動互聯網應用、商業軟件為戰略方向。以成熟的技術、創新的理念、不斷開拓進取的精神服務于各行業客戶。

上一篇:calendar.js 下一篇:apicloud
文章標簽:
文章評論:

專業的游戲開發/系統開發、品牌設計/網站建設,選仟源!

選擇專業的企業服務公司,服務更靠譜!

立即點擊咨詢>
客服圖標
客服圖標
118旺角心水论坛