乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      Android劉海屏適配全方案(華為、小米、Vivo、Oppo)

       西北望msm66g9f 2019-03-26

      多點(diǎn)在看,就是真愛!

      作者 :  快樂丸
      轉(zhuǎn)載自 :https://www.jianshu.com/p/8ead0701d8ef

      前言

      目前市面上的劉海屏和水滴屏手機(jī)越來越多了,顏值方面是因人而異,有的人覺得很好看,也有人覺得丑爆了,我個人覺得是還可以。但是作為移動開發(fā)者來說,這并不是一件好事,越來越多異形屏手機(jī)的出現(xiàn)意味著我們需要投入大量精力在適配上(就不提之后會出的折疊屏手機(jī)了)。本文總結(jié)了當(dāng)下主流手機(jī)的劉海屏適配方案,鑒于目前Android碎片化的情況,想要覆蓋所有的機(jī)型是不可能的,但是能適配一些是一些,總比什么都不做要好。

      所謂劉海屏,指的是手機(jī)屏幕正上方由于追求極致邊框而采用的一種手機(jī)解決方案。因形似劉海兒而得名——來自百度百科,水滴屏也是類似,為了簡單起見,下文就統(tǒng)稱這兩種為劉海屏了。

      什么時(shí)候需要適配

      這里先上一張官方的圖

      從圖中可以看出,劉海區(qū)域是鑲嵌在狀態(tài)欄內(nèi)部的,劉海區(qū)域的高度一般是不超過狀態(tài)欄高度的。因此,當(dāng)我們的應(yīng)用布局需要占據(jù)狀態(tài)欄來顯示時(shí),就需要考慮到劉海區(qū)域是否會遮擋住頁面上的控件或者背景,這就是為什么將狀態(tài)欄區(qū)域稱為危險(xiǎn)區(qū)域。如果應(yīng)用不需要占據(jù)狀態(tài)欄顯示,全部顯示在安全區(qū)域內(nèi),那么恭喜你,不需要做任何適配處理??偨Y(jié)來說,只有當(dāng)應(yīng)用需要全屏顯示時(shí)才需要進(jìn)行適配。
      全屏顯示無非就是兩種情況:第一種是我們常說的沉浸式狀態(tài)欄,也就是狀態(tài)欄透明,頁面的布局延伸到狀態(tài)欄顯示,這種情況下狀態(tài)欄依然可見;第二種是類似應(yīng)用的閃屏頁風(fēng)格,頁面全屏顯示,狀態(tài)欄不可見。這兩種情況下如果不進(jìn)行適配處理都會產(chǎn)生一些問題。
      先來看第一種情況,沉浸式風(fēng)格。需要將狀態(tài)欄設(shè)置為透明,需要注意只有在Android 4.4(API Level 19)以上才支持設(shè)置透明狀態(tài)欄。有兩種設(shè)置方法:
      方法一:為Activity設(shè)置style,添加一個屬性:

      <item name='android:windowTranslucentStatus'>true</item>

      方法二:在Activity的onCreate()中為Window添加Flag

      public class ImmersiveActivity extends AppCompatActivity {

          @Override
          protected void onCreate(@Nullable Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_immersive);
              // 透明狀態(tài)欄
              if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                  getWindow().addFlags(
                          WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
              }
          }
      }

      頁面的布局很簡單,只包含一個按鈕,為了明顯,我為根布局設(shè)置了一個背景。
      activity_immersive.xml

      <?xml version='1.0' encoding='utf-8'?>
      <LinearLayout xmlns:android='http://schemas./apk/res/android'
          android:id='@+id/ll_root'
          android:layout_width='match_parent'
          android:layout_height='match_parent'
          android:background='@mipmap/bg'
          android:orientation='vertical'>


          <Button
              android:layout_width='150dp'
              android:layout_height='wrap_content'
              android:layout_gravity='center_horizontal' />


      </LinearLayout>

      運(yùn)行之后發(fā)現(xiàn)按鈕會被劉海區(qū)域所遮擋,如圖所示:

      再說第二種情況,全屏風(fēng)格,狀態(tài)欄不可見。同樣有兩種設(shè)置方法:
      方法一:為Activity設(shè)置style,添加屬性:

      <item name='android:windowFullscreen'>true</item>
      <!-- 這里為了簡單,直接從style中指定一個背景 -->
      <item name='android:windowBackground'>@mipmap/bg</item>

      方法二:在Activity的OnCreate()中添加代碼:

      public class FullScreenActivity extends AppCompatActivity {

          @Override
          protected void onCreate(@Nullable Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              // 全屏顯示
              getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN |
                      View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
          }
      }

      補(bǔ)充說明一點(diǎn),現(xiàn)在的手機(jī)屏幕高寬比例越來越大,我們還需要額外做一下適配才能使應(yīng)用在所有手機(jī)上都能全屏顯示,具體方式有兩種:
      方式一:在AndroidManifest.xml中配置支持最大高寬比

      <meta-data android:name='android.max_aspect'  
          android:value='ratio_float' />

      或者

       android:maxAspectRatio='ratio_float' (API LEVEL 26)

      說明:以上兩種接口可以二選一,ratio_float = 屏幕高 / 屏幕寬 (如oppo新機(jī)型屏幕分辨率為2280 x 1080, ratio_float = 2280 / 1080 = 2.11,建議設(shè)置 ratio_float為2.2或者更大)
      方式二:在AndroidManifest.xml中配置支持分屏,注意驗(yàn)證分屏下界面兼容性

      android:resizeableActivity='true'  

      也可以通過設(shè)置targetSdkVersion>=24(即Android 7.0),該屬性的值會默認(rèn)為true,就不需要在AndroidManifest.xml中配置了。
      運(yùn)行之后,我們發(fā)現(xiàn)狀態(tài)欄的部分留出了一條黑邊,看上起很奇怪,這顯然不是我們想要的效果。

      如何適配

      上文中已經(jīng)展示了劉海屏中全屏顯示帶來的問題,那么如何去解決呢?

      1.沉浸式狀態(tài)欄的適配

      其實(shí)沉浸式狀態(tài)欄帶來的遮擋問題與劉海屏無關(guān),本質(zhì)上是由于設(shè)置了透明狀態(tài)欄導(dǎo)致布局延伸到了狀態(tài)欄中,就算是不具有劉海屏,一定程度上也會造成布局的遮擋。不過既然劉海屏是處在狀態(tài)欄當(dāng)中的,那么我們就把這種情況也包含在劉海屏的適配中。清楚了原因之后,解決起來就很簡單了,我們只需要讓控件或布局避開狀態(tài)欄顯示就可以了,具體的解決方法有三種。
      方法一.利用fitsSystemWindows屬性
      當(dāng)我們給最外層View設(shè)置了android:fitsSystemWindows='true'屬性后,當(dāng)設(shè)置了透明狀態(tài)欄或者透明導(dǎo)航欄后,就會自動給View添加paddingTop或paddingBottom屬性,這樣就在屏幕上預(yù)留出了狀態(tài)欄的高度,我們的布局就不會占用狀態(tài)欄來顯示了。
      activity_immersive.xml

      <?xml version='1.0' encoding='utf-8'?>
      <LinearLayout xmlns:android='http://schemas./apk/res/android'
          android:id='@+id/ll_root'
          android:layout_width='match_parent'
          android:layout_height='match_parent'
          android:background='@mipmap/bg'
          android:fitsSystemWindows='true'
          android:orientation='vertical'>


          <Button
              android:layout_width='150dp'
              android:layout_height='wrap_content'
              android:layout_gravity='center_horizontal' />


      </LinearLayout>

      方法二.根據(jù)狀態(tài)欄高度手動設(shè)置paddingTop
      這種方法的實(shí)現(xiàn)本質(zhì)上和設(shè)置fitsSystemWindows是一樣的,首先獲取狀態(tài)欄高度,然后設(shè)置根布局的paddingTop等于狀態(tài)欄高度就可以了,代碼如下:

      public class ImmersiveActivity extends AppCompatActivity {

          @Override
          protected void onCreate(@Nullable Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_immersive);
              // 透明狀態(tài)欄
              if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                  getWindow().addFlags(
                          WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
              }
              LinearLayout llRoot = findViewById(R.id.ll_root);
              // 設(shè)置根布局的paddingTop
              llRoot.setPadding(0, getStatusBarHeight(this), 00);
          }

          /**
           * 獲取狀態(tài)欄高度
           *
           * @param context
           * @return
           */

          public int getStatusBarHeight(Context context) {
              int statusBarHeight = 0;
              int resourceId = context.getResources().getIdentifier('status_bar_height''dimen''android');
              if (resourceId > 0) {
                  statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
              }
              return statusBarHeight;
          }
      }

      方法三.在布局中添加一個和狀態(tài)欄高度相同的View
      和前兩種方法原理類似,同樣是讓屏幕預(yù)留出狀態(tài)欄的高度,這里在根布局中添加了一個透明的View,高度和狀態(tài)欄高度相同。這種方法的好處是可以自定義填充狀態(tài)欄View的背景,更靈活地實(shí)現(xiàn)我們想要的效果。

      public class ImmersiveActivity extends AppCompatActivity {

          @Override
          protected void onCreate(@Nullable Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_immersive);
              // 透明狀態(tài)欄
              if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                  getWindow().addFlags(
                          WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
              }
              LinearLayout llRoot = findViewById(R.id.ll_root);
              View statusBarView = new View(this);
              statusBarView.setBackgroundColor(Color.TRANSPARENT);
              ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
                      ViewGroup.LayoutParams.MATCH_PARENT,
                      getStatusBarHeight(this));
              // 在根布局中添加一個狀態(tài)欄高度的View
              llRoot.addView(statusBarView, 0, lp);
          }

          /**
           * 獲取狀態(tài)欄高度
           *
           * @param context
           * @return
           */

          public int getStatusBarHeight(Context context) {
              int statusBarHeight = 0;
              int resourceId = context.getResources().getIdentifier('status_bar_height''dimen''android');
              if (resourceId > 0) {
                  statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
              }
              return statusBarHeight;
          }
      }

      適配之后成功地將控件避開了狀態(tài)欄(危險(xiǎn)區(qū)域),如下圖所示:

      2.全屏顯示的適配

      對于全屏顯示的情況,處理起來要相對麻煩一些,下面重點(diǎn)說一下這種情況下的適配方案。

      2.1.Android P及以上

      谷歌官方從Android P開始給開發(fā)者提供了劉海屏相關(guān)的API,可以通過直接調(diào)用API來進(jìn)行劉海屏的適配處理。
      通過DisplayCutout類可以獲得安全區(qū)域的范圍以及劉海區(qū)域(官方的叫法是缺口)的信息,需要注意只有API Level在28及以上才可以調(diào)用。

      /**
       * 獲得劉海區(qū)域信息
       */

      @TargetApi(28)
      public void getNotchParams() {
          final View decorView = getWindow().getDecorView();
          if (decorView != null) {
              decorView.post(new Runnable() {
                  @Override
                  public void run() {
                      WindowInsets windowInsets = decorView.getRootWindowInsets();
                      if (windowInsets != null) {
                          // 當(dāng)全屏頂部顯示黑邊時(shí),getDisplayCutout()返回為null
                          DisplayCutout displayCutout = windowInsets.getDisplayCutout();
                          Log.e('TAG''安全區(qū)域距離屏幕左邊的距離 SafeInsetLeft:' + displayCutout.getSafeInsetLeft());
                          Log.e('TAG''安全區(qū)域距離屏幕右部的距離 SafeInsetRight:' + displayCutout.getSafeInsetRight());
                          Log.e('TAG''安全區(qū)域距離屏幕頂部的距離 SafeInsetTop:' + displayCutout.getSafeInsetTop());
                          Log.e('TAG''安全區(qū)域距離屏幕底部的距離 SafeInsetBottom:' + displayCutout.getSafeInsetBottom());
                          // 獲得劉海區(qū)域
                          List<Rect> rects = displayCutout.getBoundingRects();
                          if (rects == null || rects.size() == 0) {
                              Log.e('TAG''不是劉海屏');
                          } else {
                              Log.e('TAG''劉海屏數(shù)量:' + rects.size());
                              for (Rect rect : rects) {
                                  Log.e('TAG''劉海屏區(qū)域:' + rect);
                              }
                          }
                      }
                  }
              });
          }
      }

      這里我在測試時(shí)也發(fā)現(xiàn)了一個問題,就是如果是在style中設(shè)置了全屏模式,在適配之前,頂部狀態(tài)欄區(qū)域顯示一條黑邊,這時(shí)候調(diào)用getDisplayCutout()獲取DisplayCutout對象返回的結(jié)果是null,其實(shí)這也不難理解,因?yàn)檫@時(shí)候是看不出劉海區(qū)域的,但是這樣會導(dǎo)致在適配之前無法通過DisplayCutout判斷是否存在劉海屏,只能在適配后才能獲取到劉海區(qū)域信息,因此只能對于所有設(shè)備都添加適配代碼。
      那么接下來如何進(jìn)行適配呢,Android P中增加了一個窗口布局參數(shù)屬性layoutInDisplayCutoutMode,該屬性有三個值可以?。?/p>

      LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT:默認(rèn)的布局模式,僅當(dāng)劉海區(qū)域完全包含在狀態(tài)欄之中時(shí),才允許窗口延伸到劉海區(qū)域顯示,也就是說,如果沒有設(shè)置為全屏顯示模式,就允許窗口延伸到劉海區(qū)域,否則不允許。
      LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER:永遠(yuǎn)不允許窗口延伸到劉海區(qū)域。
      LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES:始終允許窗口延伸到屏幕短邊上的劉海區(qū)域,窗口永遠(yuǎn)不會延伸到屏幕長邊上的劉海區(qū)域。

      還有一個LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS模式,目前已經(jīng)被LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES所取代,不允許使用了,這里就不提了。
      這么看可能還是有些不理解,接下來我們在一個全屏顯示的頁面分別設(shè)置三種布局模式,看看有什么區(qū)別。

      public class FullScreenActivity extends AppCompatActivity {
          @Override
          protected void onCreate(@Nullable Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                  WindowManager.LayoutParams lp = getWindow().getAttributes();
                  // 僅當(dāng)缺口區(qū)域完全包含在狀態(tài)欄之中時(shí),才允許窗口延伸到劉海區(qū)域顯示
      //            lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
                  // 永遠(yuǎn)不允許窗口延伸到劉海區(qū)域
      //            lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
                  // 始終允許窗口延伸到屏幕短邊上的劉海區(qū)域
                  lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
                  getWindow().setAttributes(lp);
              }
          }
      }

      三種模式下的顯示效果如下圖所示:

      LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT

      LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER

      LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES

      可以看出,當(dāng)在全屏顯示情況下,LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT和LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER的效果是一樣的,都是在狀態(tài)欄顯示一條黑邊,也就是不允許窗口布局延伸到劉海區(qū)域,而LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES則允許窗口布局延伸到了劉海區(qū)域,這里需要注意是短邊劉海區(qū)域,不過一般市面上的手機(jī)劉海區(qū)域都是在短邊上的,我是沒見過劉海長在“腰”上的,因此利用這個模式就實(shí)現(xiàn)適配了。
      通過之前沉浸式狀態(tài)欄的顯示效果可以看出,LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT在此時(shí)是允許窗口布局延伸到劉海區(qū)域的,因此更證實(shí)了只有在全屏顯示的情況下該模式才不允許窗口布局延伸到劉海區(qū)域。
      適配后效果如下,現(xiàn)在看起來就很舒服了:

      我這里為了簡單沒有添加任何控件,實(shí)際開發(fā)中在全屏顯示后我們?nèi)匀恍枰紤]劉海區(qū)域是否會遮擋顯示的內(nèi)容和控件,同樣需要避開危險(xiǎn)區(qū)域來顯示。做法和沉浸式狀態(tài)欄的適配相同,原理同樣是將布局下移,預(yù)留出狀態(tài)欄的高度,這里就不一一列舉了。

      2.2 Android P以下

      目前市面上的劉海屏手機(jī)可以說是琳瑯滿目,各大廠商都在追求極致的屏占比,推出的新機(jī)型也基本上都有劉海屏,針對Android P以下的手機(jī),我們只能依照各個廠商提供的適配方案來進(jìn)行適配。我也查閱了網(wǎng)上的一些適配文章,主要還是針對目前主流的手機(jī)品牌,本文總結(jié)了華為、小米、Vivo和Oppo的適配方案,其他品牌的手機(jī)之后有時(shí)間的話可能會再考慮。

      華為適配方案

      華為官方提供的適配文檔:華為劉海屏手機(jī)安卓O版本適配指導(dǎo)
      文檔中提供了很多劉海屏相關(guān)的方法,這里就不一一列舉了,著重看一下我們需要用到的方法。
      判斷是否有劉海屏

       /**
       * 判斷是否有劉海屏
       *
       * @param context
       * @return true:有劉海屏;false:沒有劉海屏
       */

      public static boolean hasNotch(Context context) {
          boolean ret = false;
          try {
              ClassLoader cl = context.getClassLoader();
              Class HwNotchSizeUtil = cl.loadClass('com.huawei.android.util.HwNotchSizeUtil');
              Method get = HwNotchSizeUtil.getMethod('hasNotchInScreen');
              ret = (boolean) get.invoke(HwNotchSizeUtil);
          } catch (ClassNotFoundException e) {
              Log.e('test''hasNotchInScreen ClassNotFoundException');
          } catch (NoSuchMethodException e) {
              Log.e('test''hasNotchInScreen NoSuchMethodException');
          } catch (Exception e) {
              Log.e('test''hasNotchInScreen Exception');
          } finally {
              return ret;
          }
      }

      應(yīng)用頁面設(shè)置使用劉海區(qū)顯示
      官方提供了兩種適配方案:
      方案一.使用新增的meta-data屬性android.notch_support,在應(yīng)用的AndroidManifest.xml中增加meta-data屬性,此屬性不僅可以針對Application生效,也可以對Activity配置生效。
      使用方式如下:

      <meta-data android:name='android.notch_support' android:value='true'/>

      可以在Application下添加,意味著該應(yīng)用的所有頁面,系統(tǒng)都不會做豎屏場景的特殊下移或者是橫屏場景的右移特殊處理。

      <application
          android:allowBackup='true'
          android:icon='@mipmap/ic_launcher'
          android:label='@string/app_name'
          android:roundIcon='@mipmap/ic_launcher_round'
          android:supportsRtl='true'
          android:theme='@style/AppTheme'>
          <meta-data
              android:name='android.notch_support'
              android:value='true' />
           ...
      </application>

      也可以針對指定的Activity添加,意味著可以針對單個頁面進(jìn)行劉海屏適配,設(shè)置了該屬性的Activity系統(tǒng)將不會做特殊處理。

      <!-- 全屏顯示頁面 -->
      <activity
          android:name='.ui.FullScreenActivity'
          android:screenOrientation='portrait'
          android:theme='@style/FullScreenTheme'>

          <meta-data
              android:name='android.notch_support'
              android:value='true' />

      </activity>

      方案二.使用給window添加新增的FLAG_NOTCH_SUPPORT
      代碼如下:

      /**
       * 設(shè)置應(yīng)用窗口在劉海屏手機(jī)使用劉海區(qū)
       * <p>
       * 通過添加窗口FLAG的方式設(shè)置頁面使用劉海區(qū)顯示
       *
       * @param window 應(yīng)用頁面window對象
       */

      public static void setFullScreenWindowLayoutInDisplayCutout(Window window) {
          if (window == null) {
              return;
          }
          WindowManager.LayoutParams layoutParams = window.getAttributes();
          try {
              Class layoutParamsExCls = Class.forName('com.huawei.android.view.LayoutParamsEx');
              Constructor con = layoutParamsExCls.getConstructor(WindowManager.LayoutParams.class);
              Object layoutParamsExObj = con.newInstance(layoutParams);
              Method method = layoutParamsExCls.getMethod('addHwFlags'int.class);
              method.invoke(layoutParamsExObj, FLAG_NOTCH_SUPPORT);
          } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException
                  | InvocationTargetException e) {
              Log.e('test''hw add notch screen flag api error');
          } catch (Exception e) {
              Log.e('test''other Exception');
          }
      }

      官方提供的所有方法我已經(jīng)放到了工具類HwNotchUtils里,可以根據(jù)需求來使用。

      小米適配方案

      小米官方提供的適配文檔:https://dev.mi.com/console/doc/detail?pId=1293

      我們同樣看一下關(guān)鍵方法。
      判斷是否有劉海屏

      /**
       * 判斷是否有劉海屏
       *
       * @param context
       * @return true:有劉海屏;false:沒有劉海屏
       */

      public static boolean hasNotch(Context context) {
          boolean ret = false;
          try {
              ClassLoader cl = context.getClassLoader();
              Class SystemProperties = cl.loadClass('android.os.SystemProperties');
              Method get = SystemProperties.getMethod('getInt', String.class, int.class);
              ret = (Integer) get.invoke(SystemProperties, 'ro.miui.notch'0) == 1;
          } catch (Exception e) {
              e.printStackTrace();
          } finally {
              return ret;
          }
      }

      應(yīng)用頁面設(shè)置使用劉海區(qū)顯示
      小米提供的適配方案同樣有兩種(meta-data和Flag),使用方法和華為類似。
      方案一.Application級別的控制接口
      在 Application 下增加一個 meta-data,用以聲明該應(yīng)用窗口是否可以延伸到狀態(tài)欄。

      <meta-data
          android:name='notch.config'
          android:value='portrait|landscape'/>

      其中,value的值可以是以下四種

      'none' 橫豎屏都不繪制耳朵區(qū)

      'portrait' 豎屏繪制到耳朵區(qū)

      'landscape' 橫屏繪制到耳朵區(qū)

      'portrait|landscape' 橫豎屏都繪制到耳朵區(qū)

      這里的耳朵區(qū)指的就是劉海區(qū)兩側(cè)的狀態(tài)欄區(qū)域

      雖然官方文檔上說的是Application級別的,但是我覺得也可以針對某一個Activity來配置,不過由于手頭上的手機(jī)條件不滿足,我并沒有驗(yàn)證,如果有小伙伴測試過的話可以反饋一下,我再修正一下這里的說法。
      方案二.Window級別的控制接口
      通過給Window添加Flag也可以實(shí)現(xiàn)將窗口布局延伸到狀態(tài)欄中顯示。

      /*劉海屏全屏顯示FLAG*/
      public static final int FLAG_NOTCH_SUPPORT = 0x00000100// 開啟配置
      public static final int FLAG_NOTCH_PORTRAIT = 0x00000200// 豎屏配置
      public static final int FLAG_NOTCH_HORIZONTAL = 0x00000400// 橫屏配置

      /**
       * 設(shè)置應(yīng)用窗口在劉海屏手機(jī)使用劉海區(qū)
       * <p>
       * 通過添加窗口FLAG的方式設(shè)置頁面使用劉海區(qū)顯示
       *
       * @param window 應(yīng)用頁面window對象
       */

      public static void setFullScreenWindowLayoutInDisplayCutout(Window window) {
          // 豎屏繪制到耳朵區(qū)
          int flag = FLAG_NOTCH_SUPPORT | FLAG_NOTCH_PORTRAIT;
          try {
              Method method = Window.class.getMethod('addExtraFlags',
                      int.class);
              method.invoke(window, flag);
          } catch (Exception e) {
              Log.e('test''addExtraFlags not found.');
          }
      }

      官方提供的所有方法我已經(jīng)放到了工具類XiaomiNotchUtils里,可以根據(jù)需求來使用。
      這里說一下我的測試情況,我是用小米8測試的,系統(tǒng)版本已經(jīng)升到了Android P,利用小米官方提供的適配方法沒有效果,只能用谷歌官方針對Android P的適配方案,這一點(diǎn)小米的官方文檔也提到了。

      至于Android P以下版本的小米手機(jī),我并沒有測試,如果有哪位大佬測試過了發(fā)現(xiàn)有問題可以反饋一下。

      Vivo、Oppo適配方案

      Vivo官方提供的適配文檔:Vivo全面屏應(yīng)用適配指南
      Oppo官方提供的適配文檔:Oppo凹形屏適配指南
      這里把Vivo和Oppo放在一起說,官方提供的資料不像華為和小米那么詳細(xì),只是提供了判斷是否有劉海屏的方法。
      Vivo判斷是否有劉海屏

      public static final int VIVO_NOTCH = 0x00000020// 是否有劉海
      public static final int VIVO_FILLET = 0x00000008// 是否有圓角

      /**
       * 判斷是否有劉海屏
       *
       * @param context
       * @return true:有劉海屏;false:沒有劉海屏
       */

      public static boolean hasNotch(Context context) {
          boolean ret = false;
          try {
              ClassLoader classLoader = context.getClassLoader();
              Class FtFeature = classLoader.loadClass('android.util.FtFeature');
              Method method = FtFeature.getMethod('isFeatureSupport'int.class);
              ret = (boolean) method.invoke(FtFeature, VIVO_NOTCH);
          } catch (ClassNotFoundException e) {
              Log.e('Notch''hasNotchAtVivo ClassNotFoundException');
          } catch (NoSuchMethodException e) {
              Log.e('Notch''hasNotchAtVivo NoSuchMethodException');
          } catch (Exception e) {
              Log.e('Notch''hasNotchAtVivo Exception');
          } finally {
              return ret;
          }
      }

      Oppo判斷是否有劉海屏

      /**
       * 判斷是否有劉海屏
       *
       * @param context
       * @return true:有劉海屏;false:沒有劉海屏
       */

      public static boolean hasNotch(Context context) {
          return context.getPackageManager().hasSystemFeature('com.oppo.feature.screen.heteromorphism');
      }

      至于全屏顯示的適配方案,通過閱讀官方文檔和網(wǎng)上的其他適配文章,我個人總結(jié)一下就是這兩種品牌的手機(jī)在設(shè)置全屏顯示時(shí)都無需做任何處理(前提是適配了全面屏,上文中提到過如何配置),也就是不會產(chǎn)生黑邊,我們只需要避免布局中的內(nèi)容或控件不被劉海區(qū)域所遮擋就可以了。具體的做法和沉浸式狀態(tài)欄的適配相同,基本原理還是將窗口布局下移,預(yù)留出狀態(tài)欄的高度。
      注:由于手頭沒有這兩種廠商的手機(jī),因此并沒有驗(yàn)證,這一點(diǎn)確實(shí)是我做得不夠嚴(yán)謹(jǐn),有好心的大佬驗(yàn)證之后歡迎指正。
      其實(shí)我本來也想列出魅族的適配方案的,但是實(shí)在是沒找到官方文檔。。。如果有知道的大佬可以提供一下,我后面會把適配方案補(bǔ)上。
      適配時(shí)的基本邏輯就是先判斷手機(jī)的品牌,這里我利用了一個開源工具類項(xiàng)目AndroidUtilCode,提供了一個獲取手機(jī)Rom信息的工具類RomUtils,用起來很方便,然后判斷是否是劉海屏,針對劉海屏手機(jī)添加適配代碼。完整的適配代碼如下所示:

      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
          // Android P利用官方提供的API適配
          WindowManager.LayoutParams lp = getWindow().getAttributes();
          // 始終允許窗口延伸到屏幕短邊上的缺口區(qū)域
          lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
          getWindow().setAttributes(lp);
      else {
          // Android P以下根據(jù)手機(jī)廠商的適配方案進(jìn)行適配
          if (RomUtils.isHuawei() && HwNotchUtils.hasNotch(this)) {
              HwNotchUtils.setFullScreenWindowLayoutInDisplayCutout(getWindow());
          } else if (RomUtils.isXiaomi() && XiaomiNotchUtils.hasNotch(this)) {
              XiaomiNotchUtils.setFullScreenWindowLayoutInDisplayCutout(getWindow());
          }
      }

      總結(jié)

      雖然文中介紹了很多適配的內(nèi)容,但其實(shí)在開發(fā)中需要我們適配劉海屏的情況并不多,只有兩種情況需要我們進(jìn)行考慮:
      1.沉浸式狀態(tài)欄,窗口布局延伸到了狀態(tài)欄中,是否會遮擋必要的內(nèi)容或控件(處在危險(xiǎn)區(qū)域)。適配方案就是將窗口布局下移,預(yù)留出狀態(tài)欄的空間。
      2.全屏顯示模式,不做適配的話狀態(tài)欄會呈現(xiàn)一條黑邊。適配方案是首先判斷系統(tǒng)版本,是Android P及以上就按照官方的API來適配,否則根據(jù)手機(jī)廠商的適配方案進(jìn)行適配。鑒于目前市面上Android P還沒有普及,為了帶來更好的用戶體驗(yàn),我們還是需要多花一些精力來適配各個手機(jī)廠商的劉海屏手機(jī)。
      最后提示一下,本文只列出了四個當(dāng)下主流手機(jī)廠商的適配方案,我自己驗(yàn)證過的只有華為和小米(只驗(yàn)證了Android P)的方案,對于Vivo和Oppo的一些結(jié)論我可能說得不對,歡迎大家指正。當(dāng)然,如果大家還需要其他廠商的適配方案,也歡迎提出,我會盡力補(bǔ)上。
      相關(guān)的代碼和工具類我已經(jīng)上傳到了github,可以下載Demo來查看,大家一起交流

      https://github.com/StephenZKCurry/NotchAdaptedTest

      ----------  END  ----------

      分享大前端、Java、跨平臺等技術(shù),

      關(guān)注職業(yè)發(fā)展和行業(yè)動態(tài)。

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多