目錄
寫在前面
一、自定義像素適配
二、百分比布局適配
三、修改系統density適配
四、劉海屏適配
4.1、谷歌官方適配策略
4.2、國內定制ROM適配策略
寫在前面
Android的螢屏適配相信大家都做過,可以說是各有各的招,對于螢屏適配真的是逢考必問,反正我每次面試都被問到了,所以它的重要性也是不言而喻了!
先來思考幾個問題吧:為什么需要做螢屏適配?我們做螢屏適配的目的是什么?做了那么久的Android了,你知道都有哪些常見的螢屏適配的方式嗎?OK,下面來一一解答:
- 原因:Android設備碎片化,導致app的界面元素在不同螢屏尺寸上顯示不一致
- 目的:讓布局,布局組件,資源,用戶界面流程,匹配不同螢屏尺寸
- 螢屏適配常見方式:
1、布局適配

2、圖片資源適配
![]()
3、用戶流程匹配
![]()
4、限定符適配

5、劉海屏適配

什么?就這?嗯,到這里........................到這里當然不能結束啦,Because we're just getting started!咱們才剛剛開始,
上面我們給出了幾個問題的答案,我們可以對照著去思考一下自己是否都了解過,下面重點要開始今天的內容了,介紹幾種常用的螢屏適配的方案,
一、自定義像素適配
核心思想:自定義View,以一個特定寬度尺寸的設備為參考,在View的加載程序,根據當前設備的實際像素換算出目標像素,再作用在控制元件上,

舉個例子:通常公司的UI設計師一般都是只出一套設計稿,可能是以目前市面上Android設備的主流解析度為參考,或者直接以iOS為參考,假設她給出的設計稿規范為720x1280,如果是運行在1080x1920的設備上,那么此時我們就需要做螢屏適配了,如果有一個按鈕的寬是360px,它運行在720x1280的設備上是占到螢屏寬度的一半,當它運行在1080x1920的設備上時變成了螢屏寬度的1/3,這種情況肯定不是我們想要的,我們需要的是它無論顯示在何種設備上都是占到螢屏寬度的1/2,所以我們就需要進行計算了,計算出來一個相對應的縮放比例,其實也很好計算,即:1080/720x360=540px,所以我們需要在1080的設備上將按鈕的寬度調整為540個像素才能使兩邊顯示的效果一致,
OK,下面我們通過自定義ViewGroup來實作螢屏適配,通過繼承系統控制元件比如LinearLayout、RelativeLayout等控制元件,在onMesure()方法中通過縮放比例來重新計算各個子View的寬高從而實作螢屏的適配,
首先我們新建一個工具類,來獲取螢屏的寬高并且計算出縮放比例:
public class ScreenUtil {
private static ScreenUtil ScreenUtil;
//這里是設計稿參考寬高
private static final float STANDARD_WIDTH = 1080;
private static final float STANDARD_HEIGHT = 1920;
//這里是螢屏顯示寬高
private int mDisplayWidth;
private int mDisplayHeight;
private ScreenUtil(Context context){
//獲取螢屏的寬高
if(mDisplayWidth == 0 || mDisplayHeight == 0){
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
if (manager != null){
DisplayMetrics displayMetrics = new DisplayMetrics();
manager.getDefaultDisplay().getMetrics(displayMetrics);
if (displayMetrics.widthPixels > displayMetrics.heightPixels){
//橫屏
mDisplayWidth = displayMetrics.heightPixels;
mDisplayHeight = displayMetrics.widthPixels;
}else{
mDisplayWidth = displayMetrics.widthPixels;
mDisplayHeight = displayMetrics.heightPixels - getStatusBarHeight(context);
}
}
}
}
//獲取狀態欄的高度
public int getStatusBarHeight(Context context){
int resID = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resID > 0){
return context.getResources().getDimensionPixelSize(resID);
}
return 0;
}
public static ScreenUtil getInstance(Context context){
if (ScreenUtil == null){
ScreenUtil = new ScreenUtil(context.getApplicationContext());
}
return ScreenUtil;
}
//獲取水平方向的縮放比例
public float getHorizontalScale(){
return mDisplayWidth / STANDARD_WIDTH;
}
//獲取垂直方向的縮放比例
public float getVerticalScale(){
return mDisplayHeight / STANDARD_HEIGHT;
}
}
接著我們來實作自定義的ViewGroup,這里繼承自系統的控制元件,在onMesure()方法中重新計算各個子控制元件的大小:
public class PixelLinearLayout extends LinearLayout {
//防止二次測量
private boolean flag;
public PixelLinearLayout(Context context) {
super(context);
}
public PixelLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PixelLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (!flag) {
float scaleX = ScreenUtil.getInstance(getContext()).getHorizontalScale();
float scaleY = ScreenUtil.getInstance(getContext()).getVerticalScale();
int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
LayoutParams params = (LayoutParams) child.getLayoutParams();
params.width = (int) (params.width * scaleX);
params.height = (int) (params.height * scaleY);
params.leftMargin = (int) (params.leftMargin * scaleX);
params.rightMargin = (int) (params.rightMargin * scaleX);
params.topMargin = (int) (params.topMargin * scaleY);
params.bottomMargin = (int) (params.bottomMargin * scaleY);
}
flag = true;
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
這里為了防止二次測量,所以加了一個flag標志位,讓計算的代碼只執行一次,這里我們是繼承自線性布局的,在實際使用程序中你可以根據需求去進行擴展,比如再實作RelativeLayout、FrameLayout等控制元件,
由于咱們的設計稿也就是標準寬高是1080x1920,所以我們可以在布局檔案中添加控制元件,在不同的設備上運行來看效果,注意:布局檔案中控制元件的大小填入的單位需要使用px,因為我們獲取的螢屏尺寸單位是以像素為單位的:
<?xml version="1.0" encoding="utf-8"?>
<com.jarchie.androidui.screenadapter.pixel.PixelLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="540px"
android:layout_height="200px"
android:layout_marginTop="30dp"
android:background="@color/colorAccent"
android:gravity="center"
android:text="自定義像素適配"
android:textColor="#fff" />
</com.jarchie.androidui.screenadapter.pixel.PixelLinearLayout>
由于手里沒那么多真機,所以就簡單的使用模擬器來進行測驗了,這里我分別啟動了1080x1920和720x1280的兩個模擬器:

在1080x1920和720x1280的兩臺設備上的效果如下圖所示,都是占了螢屏寬度的一半:

二、百分比布局適配
核心思想:以父容器尺寸作為參考,在View的加載程序,根據當前父容器實際尺寸換算出目標尺寸,再作用在View上,

使用百分比布局適配,我們無需知道設計稿的尺寸,我們只需要知道控制元件占用螢屏寬高的比例即可,
首先需要在build.gradle檔案中添加百分比布局的依賴:
//谷歌百分比布局
implementation 'com.android.support:percent:28.0.0'
接著在布局檔案中來使用百分比布局,在其中放置一個TextView寬高我們設定各占螢屏的一半,注意這里需要添加命名空間,百分比布局的屬性都需要通過app:xxx來設定,如果你不使用命名空間,那么它就和普通的布局沒有任何區別,它只是對普通的布局進行了擴充:
<?xml version="1.0" encoding="utf-8"?>
<android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/colorAccent"
android:gravity="center"
android:text="百分比布局適配"
android:textColor="#fff"
app:layout_widthPercent="50%"
app:layout_heightPercent="50%"/>
</android.support.percent.PercentRelativeLayout>
效果如下:

這個就是谷歌官方推出的一個百分比布局適配的庫,現在使用好像是會劃橫線告訴你它已經過時了,不過依然不影響你使用,如果你確實看著不爽,那么你可以自己實作這樣一套百分比布局適配的解決方案,其實還是自定義ViewGroup,
這里簡單說一下思路,具體的代碼就不寫了,大家可以參考谷歌官方的百分比布局來寫:
- 新建PercentLayout繼承自系統控制元件RelativeLayout,對系統控制元件進行擴展
- 在values檔案夾下新建attrs.xml檔案,撰寫自定義屬性,就是類似于谷歌官方的app:layout_widthPercent這種形式的
- 在PercentLayout類中新建LayoutParams這個布局屬性內部類,繼承自RelativeLayout.LayoutParams這個類,保證原有屬性可用,然后決議上面自定義的屬性
- 在onMesure()方法中進行布局屬性的獲取以及寬高的二次計算
三、修改系統density適配
核心思想:修改density、scaleDensity、densityDpi值,直接更改系統內部對于目標尺寸而言的像素密度,

關于density、scaleDensity、densityDpi這些概念以及它們之間的關系,我找了兩篇文章給大家做參考:
《Android:sp與dp(densityDpi與scaledDensity)》
《dpi 、 dip 、解析度、螢屏尺寸、px、density 關系以及換算》
接下來我們來看看在代碼中究竟該如何實作呢?
首先,來寫一個工具類,我們給出一個參考設備的寬度,以及螢屏密度和字體縮放比例,然后獲取到當前螢屏的顯示資訊,來計算出目標值density、scaleDensity和densityDpi,并將目標值替換原有的螢屏顯示資訊:
public class Density {
private static final float WIDTH = 360;//參考設備的寬,單位是dp 360 / 2 = 180
private static float appDensity;//表示螢屏密度
private static float appScaleDensity; //字體縮放比例,默認appDensity
public static void setDensity(final Application application, Activity activity){
//獲取當前app的螢屏顯示資訊
DisplayMetrics displayMetrics = application.getResources().getDisplayMetrics();
if (appDensity == 0){
//初始化賦值操作
appDensity = displayMetrics.density;
appScaleDensity = displayMetrics.scaledDensity;
//添加字體變化監聽回呼
application.registerComponentCallbacks(new ComponentCallbacks() {
@Override
public void onConfigurationChanged(Configuration newConfig) {
//字體發生更改,重新對scaleDensity進行賦值
if (newConfig != null && newConfig.fontScale > 0){
appScaleDensity = application.getResources().getDisplayMetrics().scaledDensity;
}
}
@Override
public void onLowMemory() {
}
});
}
//計算目標值density, scaleDensity, densityDpi
float targetDensity = displayMetrics.widthPixels / WIDTH; // 1080 / 360 = 3.0
float targetScaleDensity = targetDensity * (appScaleDensity / appDensity);
int targetDensityDpi = (int) (targetDensity * 160);
//替換Activity的density, scaleDensity, densityDpi
DisplayMetrics dm = activity.getResources().getDisplayMetrics();
dm.density = targetDensity;
dm.scaledDensity = targetScaleDensity;
dm.densityDpi = targetDensityDpi;
}
}
上面的代碼中我們還添加了系統設定中字體變化前后的一個監聽,
OK,一個工具類就可以搞定啦,實際專案中使用時,由于我們的頁面肯定不止一個,所以通常情況下你可以使用如下兩種方式來進行替換:
①、全域Application
自定義Application類,在onCreate()方法中監聽Activity創建的回呼,同時別忘記在Manifest.xml清單檔案中設定我們自己的Application類:
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
Density.setDensity(App.this, activity);
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
});
}
}
②、定義基類BaseActivity
定義BaseActivity在該類中統一處理,注意:設定的代碼必須寫在setContentView之前:
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Density.setDensity(getApplication(),this);
}
}
public class MainActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_screen_adapter_layout);
}
}
接著,我們來做個測驗,由于我們設定的參考設備的寬是360dp,所以假設我們想讓控制元件占用螢屏的一半,可以直接在布局檔案中設定寬度為180dp即可:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/text1"
android:layout_width="180dp"
android:layout_height="180dp"
android:text="左上"
android:gravity="center"
android:textColor="#fff"
android:textSize="18sp"
android:background="@color/colorAccent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="180dp"
android:layout_height="180dp"
android:text="右下"
android:gravity="center"
android:textColor="#fff"
android:textSize="18sp"
android:background="@color/colorAccent"
app:layout_constraintLeft_toRightOf="@id/text1"
app:layout_constraintTop_toBottomOf="@id/text1" />
</android.support.constraint.ConstraintLayout>
運行的效果如下:

實際專案中推薦使用這種方案進行螢屏適配,使用方式簡單,侵入性低,可以適配三方庫和系統控制元件,并且也不會有性能損耗,其實這也就是今日頭條適配方案,相信大家也都聽說過這個方案了,當時出來的時候火了一段時間,我特地去把原文翻出來🤲雙手奉上:
位元組跳動技術團隊:《一種極低成本的Android螢屏適配方式》
我這里還為大家準備了另一種類似的解決方案,推薦閱讀:《Android 螢屏適配終結者》
四、劉海屏適配
4.1、谷歌官方適配策略
Android官方9.0劉海屏適配策略
- 如果非全屏模式(有狀態欄),則app不受劉海屏的影響,劉海屏的高就是狀態欄的高
- 如果全屏模式,app未適配劉海屏,系統會對界面做特殊處理,豎屏向下移動,橫屏向右移動
針對上面說的這兩種情況,我們通過一個實際的專案來看下效果:
首先將模擬器的開發者選項打開,在開發者模式下找到模擬具有凹口的顯示屏,選擇長型顯示屏凹口,這樣就把我們的模擬器調整為了具有劉海屏的顯示幕:

然后準備一張圖片,然后撰寫布局檔案,我們讓圖片橫向豎向都充滿螢屏:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:scaleType="fitXY"
android:src="@drawable/zly" />
</RelativeLayout>
然后在Activity代碼中什么都不做,直接啟動這個頁面,第一種情況運行的結果如下圖所示,圖片在下方填充,上方狀態欄和劉海屏有重疊,這種效果也就是默認會出現的效果:

第二種情況是全屏模式,我們先來考慮第一種應用場景:app某個詳情頁面的某張圖片點擊查看大圖,一般都是跳轉到新的頁面讓它全屏展示,這種情況該如何做?
首先我們需要將螢屏設定為全屏:
//設定全屏,注意這幾行代碼需要在setContentView之前執行
requestWindowFeature(Window.FEATURE_NO_TITLE);
Window window = getWindow();
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_screen_adapter_layout);
效果如下圖所示:

在螢屏上方出現了一條黑邊,這是系統為它做的默認的處理,表示如果當前界面為全屏模式,它會將內容區域向下移動,上面這條黑邊就會空出來,此時我們就需要對螢屏進行適配了,我們需要將內容區域延伸進劉海區域,
接著來看谷歌官方9.0對它的處理,主要用到的是WindowManager.LayoutParams類里面的LayoutInDisplayCutoutMode這個值,它是Android9.0之后對劉海屏支持的屬性,具體都有哪些值我們來看一下:
//讓內容區域延伸進劉海
WindowManager.LayoutParams params = window.getAttributes();
/**
* @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT 全屏模式,內容下移,非全屏不受影響
* @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES 允許內容延伸進劉海區
* @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER 不允許內容延伸進劉海區
*/
params.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
window.setAttributes(params);
我們設定這個值允許內容延伸進劉海,結果如下圖所示:

這里上面狀態欄變成了白色,有人會說了你這內容區還是在下方啊,也沒見它延伸到劉海中啊?事實上它已經延伸進去了,這個白色是因為頂層布局的背景是白色的,所以我們還需要再做一步操作,將當前頁面設定為沉浸式模式:
//設定成沉浸式
int flags = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
int visibility = window.getDecorView().getSystemUiVisibility();
visibility |= flags; //追加沉浸式設定
window.getDecorView().setSystemUiVisibility(visibility);
設定完成之后運行的效果就是我們想要的了:

在實際專案開發中,對于官方的Android9.0的適配,我們首先應該考慮它是否有劉海屏,所以為了邏輯的完整性以及代碼的健壯性,我們再來寫一個判斷是否有劉海的方法:
private boolean hasDisplayCutout(Window window) {
DisplayCutout displayCutout;
View rootView = window.getDecorView();
WindowInsets insets = rootView.getRootWindowInsets();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && insets != null){
displayCutout = insets.getDisplayCutout();
if (displayCutout != null){
if (displayCutout.getBoundingRects() != null && displayCutout.getBoundingRects().size() > 0 && displayCutout.getSafeInsetTop() > 0){
return true;
}
}
}
return false;
}
OK,這樣一種場景的劉海屏適配我們就搞定啦!
別急著走,此時如果我們把xml布局中的圖片給換掉,我們換成一個文本,讓它居中顯示,請問會出現什么問題?
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/content">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_centerHorizontal="true"
android:background="@color/colorAccent"
android:gravity="center"
android:text="Hello Android!"
android:textColor="#fff" />
</RelativeLayout>
沒錯,它就變成了這個樣子:

這種情況很明顯,我們的TextView被劉海區域給擋住了,所以此時就不能只是簡單的把內容區域延伸進劉海了,解決方案一般有兩種:
①、UI在出設計稿的時候盡量規避這種情況,不要把內容放到這個區域,這種方案以實際公司業務為準,這里不多說了;
②、通過代碼把內容向下移動,獲取劉海屏的高度,讓內容下移對應的高度,一般情況下,劉海的高度等于狀態欄的高度,所以我們可以直接取狀態欄的高度,
對于②我們來實際操作一下,首先獲取劉海的高度:
//通常情況下,劉海的高就是狀態欄的高
public int heightForDisplayCutout(){
int resID = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resID > 0){
return getResources().getDimensionPixelSize(resID);
}
return 96;
}
然后將內容下移對應的高度:
//將內容區域下移劉海的高度
TextView text= findViewById(R.id.text);
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) text.getLayoutParams();
layoutParams.topMargin = heightForDisplayCutout();
text.setLayoutParams(layoutParams);
或者通過父容器去設定它的Padding:
RelativeLayout layout = findViewById(R.id.content);
layout.setPadding(layout.getPaddingLeft(), heightForDisplayCutout(), layout.getPaddingRight(), layout.getPaddingBottom());

OK,上面這些就是對官方劉海屏的一個適配的程序了,我們再來總結一下操作步驟:
- 判斷手機是否有劉海
- 設定是否讓內容區域延伸進劉海
- 設定控制元件是否避開劉海區域
- 獲取劉海的高度
4.2、國內定制ROM適配策略
在實際專案開發程序中,我們需要適配的手機大多都是國內的一些手機廠商他們的產品,比如:華為、小米、OPPO、VIVO...等等,由于國內的手機系統一般都是各家廠商定制的ROM,所以適配這些手機我們需要去針對性的適配,下面為大家整理了國內四大廠商的官方檔案地址,大家可以對照著參考官方檔案去做適配:
- 華為(好像打不開了):https://devcenter-test.huawei.com/consumer/cn/devservice/doc/50114
- 花粉俱樂部:https://club.huawei.com/thread-15620158-1-1.html
- 小米:https://dev.mi.com/console/doc/detail?pId=1293
- oppo:https://open.oppomobile.com/service/message/detail?id=61876
- vivo:https://dev.vivo.com.cn/documentCenter/doc/103
這里給大家提供了一個工具類,方便呼叫:
public class Utils {
/****************************************華為手機***********************************************/
/**
* 是否劉海
* @param context
* @return
*/
public static boolean hasNotchInScreen(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");
}
return ret;
}
/**
* 獲取劉海尺寸:width、height,int[0]值為劉海寬度 int[1]值為劉海高度,
* @param context
* @return
*/
public static int[] getNotchSize(Context context) {
int[] ret = new int[]{0, 0};
try {
ClassLoader cl = context.getClassLoader();
Class HwNotchSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");
Method get = HwNotchSizeUtil.getMethod("getNotchSize");
ret = (int[]) get.invoke(HwNotchSizeUtil);
} catch (ClassNotFoundException e) {
Log.e("test", "getNotchSize ClassNotFoundException");
} catch (NoSuchMethodException e) {
Log.e("test", "getNotchSize NoSuchMethodException");
} catch (Exception e) {
Log.e("test", "getNotchSize Exception");
}
return ret;
}
/**
* 設定使用劉海區域
* @param window
*/
public static void setFullScreenWindowLayoutInDisplayCutout(Window window) {
if (window == null) {
return;
}
try {
WindowManager.LayoutParams layoutParams = window.getAttributes();
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 (Exception e) {
Log.e("test", "other Exception");
}
}
/*劉海屏全屏顯示FLAG*/
public static final int FLAG_NOTCH_SUPPORT = 0x00010000;
/**
* 設定應用視窗在華為劉海屏手機不使用劉海
*
* @param window 應用頁面window物件
*/
public static void setNotFullScreenWindowLayoutInDisplayCutout(Window window) {
if (window == null) {
return;
}
try {
WindowManager.LayoutParams layoutParams = window.getAttributes();
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("clearHwFlags", int.class);
method.invoke(layoutParamsExObj, FLAG_NOTCH_SUPPORT);
} catch (Exception e) {
Log.e("test", "hw clear notch screen flag api error");
}
}
/****************************************OPPO手機***********************************************/
/**
* 判斷該 OPPO 手機是否為劉海屏手機
* @param context
* @return
*/
public static boolean hasNotchInOppo(Context context) {
return context.getPackageManager().hasSystemFeature("com.oppo.feature.screen.heteromorphism");
}
/**
* 劉海高度和狀態欄的高度是一致的
* @param context
* @return
*/
public static int getStatusBarHeight(Context context) {
int resId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resId > 0){
return context.getResources().getDimensionPixelSize(resId);
}
return 0;
}
/****************************************VIVO手機***********************************************/
/**
* Vivo判斷是否有劉海, VIVO的劉海高度小于等于狀態欄高度
*/
public static final int VIVO_NOTCH = 0x00000020;//是否有劉海
public static final int VIVO_FILLET = 0x00000008;//是否有圓角
public static boolean hasNotchAtVivo(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;
}
}
}
OK,到了這里今天的內容就說的差不多了,咱們下期再會!
祝:作業順利!
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/225875.html
標籤:其他
上一篇:[OpenGL ES] 自定義GLSurfaceView
下一篇:Android知識總結
