欧美一级特黄大片做受成人-亚洲成人一区二区电影-激情熟女一区二区三区-日韩专区欧美专区国产专区

Android透明化和沉浸式狀態(tài)欄實(shí)踐及源碼分析

本文所提到的透明狀態(tài)欄其實(shí)指的是將頂部的導(dǎo)航欄延伸到狀態(tài)欄,使之渾然一體(Google官方建議狀態(tài)欄顏色比導(dǎo)航欄的顏色略深一點(diǎn)),并不代表一定不設(shè)置背景色,比如導(dǎo)航欄是白色,則可設(shè)置狀態(tài)欄為白色,視情況而定。

創(chuàng)新互聯(lián)專注于五河企業(yè)網(wǎng)站建設(shè),響應(yīng)式網(wǎng)站,商城網(wǎng)站建設(shè)。五河網(wǎng)站建設(shè)公司,為五河等地區(qū)提供建站服務(wù)。全流程按需網(wǎng)站建設(shè),專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)

相比于iOS系統(tǒng),Android系統(tǒng)對(duì)于狀態(tài)欄的設(shè)置就顯得稍微復(fù)雜了一點(diǎn)。Android系統(tǒng)提供了API 19以上對(duì)狀態(tài)欄的設(shè)置接口,而直到API 23以上才提供對(duì)于icon顏色的設(shè)置,還有就是各家廠商(如魅族,小米等)對(duì)于狀態(tài)欄的有自己的定制,對(duì)于需要使用淺色背景狀態(tài)欄的應(yīng)用,沒處理好的話往往導(dǎo)致淺色背景,白色icon ,狀態(tài)欄不分你我的悲劇。。

Android透明化和沉浸式狀態(tài)欄實(shí)踐及源碼分析

(內(nèi)心os:嗯?右上角那一個(gè)綠色的電池,用戶一定知道他是狀態(tài)欄對(duì)吧。)

我隨即對(duì)比了一些主流app,發(fā)現(xiàn)在我的魅藍(lán)2(Android 5.1 Flyme 4.5)上竟然都不支持透明狀態(tài)欄,這對(duì)于我這種追求審美的人(其實(shí)是視覺提的需求)來說簡(jiǎn)直不能忍。在我折騰了幾天之后,終于解決了這些問題,希望對(duì)大家思路有一些幫助。

言歸正傳,本文主要針對(duì)以下幾點(diǎn)進(jìn)行分析:1.是否隱藏狀態(tài)欄(全屏模式) 2.狀態(tài)欄的背景色的設(shè)置 3.狀態(tài)欄icon的顏色的設(shè)置,而對(duì)于透明狀態(tài)欄設(shè)置過程中,可能造成的icon顏色設(shè)置成功,而背景顏色設(shè)置失敗,等等原因造成的淺色底,淺色字或深色底,深色字等錯(cuò)誤情況的處理及兜底方案 4.源碼實(shí)現(xiàn)。

下面我們就以上幾點(diǎn)來討論一下Android中透明狀態(tài)欄的實(shí)現(xiàn)。

1. 全屏模式(沉浸式狀態(tài)欄)

這種情況其實(shí)用得并不多,基本上使用場(chǎng)景在閃屏頁(yè)展示廣告或logo,以及一些閱讀類app需要盡可能的利用到屏幕大小,展示更多的內(nèi)容。

Android透明化和沉浸式狀態(tài)欄實(shí)踐及源碼分析

設(shè)置方法很簡(jiǎn)單,分兩種,在API > 16時(shí):

theme中定義:將自定義該Activtiy的theme,并在其中添加

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

代碼中定義:

復(fù)制代碼 代碼如下:

window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)

2. 狀態(tài)欄的背景色

我們都知道,在Android5.0(即API > 21)時(shí),Google官方提供接口設(shè)置對(duì)應(yīng)的狀態(tài)欄背景色

window.setStatusBarColor(@ColorInt int color);

那我們要想在Android5.0以下設(shè)置背景色就真的沒有辦法了嗎?并不是,我們發(fā)現(xiàn)在Android4.4以后,出現(xiàn)了windowTranslucentStatus這一特性,所以思路如下:

  • 先設(shè)置狀態(tài)欄的透明屬性
  • 給根布局頂部加上一個(gè)和狀態(tài)欄一樣大小的矩形View,充當(dāng)假的狀態(tài)欄
  • 設(shè)置FitsSystemWindows屬性為true,此時(shí)跟布局會(huì)延伸到狀態(tài)欄,處在頂部位置就是之前設(shè)置的view,這樣就可以以假亂真了。 
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
   // 設(shè)置狀態(tài)欄透明
   activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
   // 生成一個(gè)狀態(tài)欄大小的矩形View
   View statusView = createStatusView(activity, color);
   ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
   decorView.addView(statusView);
   // 設(shè)置根布局的參數(shù)
   ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
   rootView.setFitsSystemWindows(true);
   rootView.setClipToPadding(true);
  }

設(shè)置矩形色塊的代碼如下:

 private static View createStatusView(Activity activity, int color) {
  // 獲得狀態(tài)欄高度
  int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android");
  int statusBarHeight = activity.getResources().getDimensionPixelSize(resourceId);

  // 繪制一個(gè)和狀態(tài)欄一樣高的矩形
  View statusView = new View(activity);
  LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
    statusBarHeight);
  statusView.setLayoutParams(params);
  statusView.setBackgroundColor(color);
  return statusView;
 }

效果如下:

Android透明化和沉浸式狀態(tài)欄實(shí)踐及源碼分析

3. 狀態(tài)欄icon的顏色

我們通過調(diào)研發(fā)現(xiàn),魅族和小米的兩個(gè)廠商分別對(duì)于Flyme及MIUI做了狀態(tài)欄的定制,所以我們可以在Flyme4.0以上及MIUIV6以上都可以實(shí)現(xiàn)icon顏色的替換,結(jié)合上述的狀態(tài)欄背景色的替換,我們就可以對(duì)于魅族及小米兩款機(jī)型適配淺色的導(dǎo)航欄了。

3.1 魅族Flyme4.0+

 /**
  * 魅族Flyme4+以上透明狀態(tài)欄
  * @param window 
  * @param dark 是否把狀態(tài)欄字體及圖標(biāo)顏色設(shè)置為深色
  * @return 是否設(shè)置成功
  */
 public static boolean setMeizuStatusBarDarkIcon(Window window, boolean dark) {
  boolean result = false;
  if (window != null) {
   try {
    WindowManager.LayoutParams lp = window.getAttributes();
    Field darkFlag = WindowManager.LayoutParams.class
      .getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
    Field meizuFlags = WindowManager.LayoutParams.class
      .getDeclaredField("meizuFlags");
    darkFlag.setAccessible(true);
    meizuFlags.setAccessible(true);
    int bit = darkFlag.getInt(null);
    int value = meizuFlags.getInt(lp);
    if (dark) {
     value |= bit;
    } else {
     value &= ~bit;
    }
    meizuFlags.setInt(lp, value);
    window.setAttributes(lp);
    result = true;
   } catch (Exception e) {
    //錯(cuò)誤處理
   }
  }
  return result;
 }

3.2 MIUIV6.0+

 /**
  * 設(shè)置狀態(tài)欄字體圖標(biāo)為深色,需要MIUIV6以上
  * @param window
  * @param dark 是否把狀態(tài)欄字體及圖標(biāo)顏色設(shè)置為深色
  * @return 是否設(shè)置成功
  *
  */
 public static boolean setMIUIStatusBarLightMode(Window window, boolean dark) {
  boolean result = false;
  if (window != null) {
   Class clazz = window.getClass();
   try {
    int darkModeFlag = 0;
    Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
    Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
    darkModeFlag = field.getInt(layoutParams);
    Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
    if(dark){
     extraFlagField.invoke(window,darkModeFlag,darkModeFlag);//狀態(tài)欄透明且黑色字體
    }else{
     extraFlagField.invoke(window, 0, darkModeFlag);//清除黑色字體
    }
    result=true;
   }catch (Exception e){
    //錯(cuò)誤處理
   }
  }
  return result;
 }

效果如下:

Android透明化和沉浸式狀態(tài)欄實(shí)踐及源碼分析

4. 設(shè)置背景色失敗處理方案

4.1 解決思路

說了那么多,有童鞋可能就問了,上述說的代碼可能對(duì)大多數(shù)機(jī)型有效,但是萬(wàn)一某些機(jī)型設(shè)置失敗了呢?體驗(yàn)不但沒提升,反倒讓用戶不能忍受了。

好吧,下面我們來看一下應(yīng)該怎么解決。思路如下:

  • 獲取狀態(tài)欄的背景色,與需要設(shè)置的顏色進(jìn)行比對(duì),如果相等則設(shè)置成功。
  • 如果背景色設(shè)置失敗,針對(duì)非Flyme機(jī)型,我們回退狀態(tài)欄icon顏色,還原到最初狀態(tài)。
  • 上述為什么要強(qiáng)調(diào)非Flyme機(jī)型,是因?yàn)橛眠^Flyme機(jī)型的童鞋肯定知道有一個(gè)透明狀態(tài)欄的開關(guān),當(dāng)開啟后,F(xiàn)lyme會(huì)對(duì)狀態(tài)欄進(jìn)行處理,但處理效果并不和我們想要的一樣(比如視圖頂部為黑色背景,但是卻設(shè)置了白色狀態(tài)欄),并且兩者都對(duì)狀態(tài)欄進(jìn)行操作,操作的沖突也有可能引起上述的icon顏色不明顯的情況,所以我們自己定義一個(gè)ColorDrawable進(jìn)行替換。

4.2 獲取狀態(tài)欄顏色

 /**
  * 獲取狀態(tài)欄顏色
  *
  * @param window 需要獲取的activity
  * @return color 狀態(tài)欄的顏色值
  */
 public static int getColor(Window window){
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
   return window.getStatusBarColor();
  }else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
   ViewGroup decorView = (ViewGroup) window.getDecorView();
   int count = decorView.getChildCount();
   if (count > 0 && decorView.getChildAt(count - 1) instanceof StatusBarView) {
    int color = Color.TRANSPARENT;
    Drawable background = decorView.getChildAt(count - 1).getBackground();
    if (background instanceof ColorDrawable)
     color = ((ColorDrawable) background).getColor();
    return color;
   }
  }
  return -1;
 }

下面講一下針對(duì)如何屏蔽Flyme機(jī)型沉浸式狀態(tài)欄打開時(shí)的影響:

 private static boolean setMeizuStatusColor(Window window, @ColorInt int color, int alpha) {
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
   final View view = window.getDecorView().findViewById(android.R.id.statusBarBackground);
   if (view != null) {
    if (!(view.getBackground() instanceof MeizuColorDrawable)) {
      //替換成自定義ColorDrawable
     view.setBackground(new MeizuColorDrawable());
    }

    if (view.getBackground() instanceof MeizuColorDrawable) {
     MeizuColorDrawable colorDrawable = (MeizuColorDrawable) view.getBackground();
     int finalColor = StatusBarUtil.calculateStatusColor(color, alpha);
     view.setBackgroundColor(finalColor);
     colorDrawable.setColor(finalColor);
     return true;
    }
   }
  }
  return false;
 }

4.3 兜底方案

上面提到了一些失敗判斷途徑,那么如果還是對(duì)一些機(jī)型無(wú)效呢?我的建議是可以在本地預(yù)埋一個(gè)開關(guān),萬(wàn)一出問題,服務(wù)器可以對(duì)相應(yīng)機(jī)型的沉浸式開關(guān)關(guān)掉即可,或者采用hotfix等方式來進(jìn)行修復(fù)。

5. 源碼分析

看過上面針對(duì)魅族和小米設(shè)置icon的源代碼我們知道,共同點(diǎn)都是通過反射來進(jìn)行調(diào)用相應(yīng)的方法,或者對(duì)于相應(yīng)的狀態(tài)欄視圖進(jìn)行設(shè)置顏色。我們來看看源碼,在Window中有一個(gè)抽象方法是setStatusBarColor而在PhoneWindow中我們找到了該方法的實(shí)現(xiàn)

 @Override
 public void setStatusBarColor(int color) {
  mStatusBarColor = color;
  mForcedStatusBarColor = true;
  if (mDecor != null) {
   mDecor.updateColorViews(null, false /* animate */);
  }
 }

mDecor.updateColorViews()

  private WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
  ...
     updateColorViewInt(mNavigationColorViewState, sysUiVisibility, mNavigationBarColor,
      navBarSize, navBarToRightEdge, 0 /* rightInset */,
      animate && !disallowAnimate);

    boolean statusBarNeedsRightInset = navBarToRightEdge
      && mNavigationColorViewState.present;
    int statusBarRightInset = statusBarNeedsRightInset ? mLastRightInset : 0;
    updateColorViewInt(mStatusColorViewState, sysUiVisibility, mStatusBarColor,
      mLastTopInset, false /* matchVertical */, statusBarRightInset,
      animate && !disallowAnimate);
  ...
}

那么mNavigationColorViewState和mStatusColorViewState又是什么呢?別著急,我們繼續(xù)往下看

  private final ColorViewState mStatusColorViewState = new ColorViewState(
    SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
    Gravity.TOP,
    Gravity.LEFT,
    STATUS_BAR_BACKGROUND_TRANSITION_NAME,
    com.android.internal.R.id.statusBarBackground,
    FLAG_FULLSCREEN);
  private final ColorViewState mNavigationColorViewState = new ColorViewState(
    SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
    Gravity.BOTTOM,
    Gravity.RIGHT,
    NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
    com.android.internal.R.id.navigationBarBackground,
    0 /* hideWindowFlag */);

mNavigationColorViewState和mStatusColorViewState這兩個(gè)都是ColorViewState定義好的ColorViewState,主要存儲(chǔ)了關(guān)于statusbar的一些視圖信息。包括id,translucentFlag,verticalGravity等等。

接著看updateColorViewInt()方法,這個(gè)方法是對(duì)一個(gè)View設(shè)置顏色的,自然這個(gè)View也就是指的狀態(tài)欄的View,下面來看一下實(shí)現(xiàn):

private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
    int size, boolean verticalBar, int rightMargin, boolean animate) {
    state.present = size > 0 && (sysUiVis & state.systemUiHideFlag) == 0
     && (getAttributes().flags & state.hideWindowFlag) == 0
     && (getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
   boolean show = state.present
     && (color & Color.BLACK) != 0
     && (getAttributes().flags & state.translucentFlag) == 0;

   boolean visibilityChanged = false;
   View view = state.view;

   int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size;
   int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT;
   int resolvedGravity = verticalBar ? state.horizontalGravity : state.verticalGravity;

    //對(duì)傳入的ColorViewState的view做非空判斷
   if (view == null) {
    if (show) {
     //新建一個(gè)view
     state.view = view = new View(mContext);
     //設(shè)置背景色
     view.setBackgroundColor(color);
     view.setTransitionName(state.transitionName);
     view.setId(state.id);
     visibilityChanged = true;
     //有人可能會(huì)感覺奇怪,為什么show為true的時(shí)候,這里反倒要設(shè)置INVISIBLE。從后面代碼可以知道,這里無(wú)非是想通過動(dòng)畫來做一個(gè)漸顯操作,而不是立馬顯示出來。
     view.setVisibility(INVISIBLE);
     state.targetVisibility = VISIBLE;

     LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight,
       resolvedGravity);
     lp.rightMargin = rightMargin;
     addView(view, lp);
     updateColorViewTranslations();
    }
   } else {
    int vis = show ? VISIBLE : INVISIBLE;
    visibilityChanged = state.targetVisibility != vis;
    state.targetVisibility = vis;
    if (show) {
     LayoutParams lp = (LayoutParams) view.getLayoutParams();
     if (lp.height != resolvedHeight || lp.width != resolvedWidth
       || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin) {
      lp.height = resolvedHeight;
      lp.width = resolvedWidth;
      lp.gravity = resolvedGravity;
      lp.rightMargin = rightMargin;
      view.setLayoutParams(lp);
     }
     //設(shè)置背景色
     view.setBackgroundColor(color);
    }
   }
   //改變狀態(tài)欄visibility屬性時(shí)加入動(dòng)畫
   if (visibilityChanged) {
    view.animate().cancel();
    if (animate) {
     if (show) {
      //顯示時(shí)先設(shè)置為全透明
      if (view.getVisibility() != VISIBLE) {
       view.setVisibility(VISIBLE);
       view.setAlpha(0.0f);
      }
      //關(guān)于透明度的動(dòng)畫插值器
      view.animate().alpha(1.0f).setInterpolator(mShowInterpolator).
        setDuration(mBarEnterExitDuration);
     } else {
      view.animate().alpha(0.0f).setInterpolator(mHideInterpolator)
        .setDuration(mBarEnterExitDuration)
        .withEndAction(new Runnable() {
         @Override
         public void run() {
          state.view.setAlpha(1.0f);
          state.view.setVisibility(INVISIBLE);
         }
        });
     }
    } else {
     view.setAlpha(1.0f);
     view.setVisibility(show ? VISIBLE : INVISIBLE);
    }
   }
  }

6. 總結(jié)

話說了這么多,其實(shí)就是想讓大家知道為什么很多app放棄在較低版本的Android系統(tǒng)對(duì)狀態(tài)欄進(jìn)行視覺修改,尤其是淺色導(dǎo)航欄的應(yīng)用。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。

分享名稱:Android透明化和沉浸式狀態(tài)欄實(shí)踐及源碼分析
文章起源:http://aaarwkj.com/article26/pcscjg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計(jì)公司、Google手機(jī)網(wǎng)站建設(shè)、小程序開發(fā)、網(wǎng)站策劃、微信小程序

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站網(wǎng)頁(yè)設(shè)計(jì)
天堂社区人妻在线亚洲| 上海老熟女啪啪露脸高潮| 在线观看男人的天堂av| 日韩天堂视频在线播放| 亚洲精品第一页中文字幕| 日韩在线欧美在线一区二区| 欧美日韩国产特级性生活| 福利在线视频一区二区| 蜜臀久久精品国产综合| 国产一级夫妻性生活欧美| 成年人免费在线观看国产| 我想看日韩一级黄色片| 亚洲高清有码在线观看| 欧美日韩一区二区三区色| 中文字幕中文字幕乱码| 国产免费成人在线视频| 一区二区三区高清av在线| 久久精品亚洲精品毛片| 免费的一区二区中文字幕| 看夫妻性生活免费视频| 免费观看国产性生活片| 男人天堂av一区二区| 亚洲欧洲日韩另类在线| 久久综合伊人欧美精品| 国产真实内射在线观看| 91激情黑丝在线观看| 91超碰这里只有精品国产| 日日激情综合久久一区| 欧美高清在线观看一区二区| 日本成人高清一区二区| 夫妻性生活视频全过程| 一区二区三区日韩欧美在线| 国产精品呻吟久久人妻| 欧美精品成人免费在线| 日本熟妇中文字幕三级久久| 国产91对白在线观看| 中文一级伦理一区二区| 欧美av精品一区二区三区| 日韩黄片大全在线观看| 欧美一区二区在线精品| 欧美日韩亚洲精品内裤|