為什么要屏幕適配?
因為Android設備的碎片化嚴重,導致app的界面元素在不同分辨率的設備屏幕尺寸上顯示不一致;
為了讓布局、布局組件、資源、用戶界面流程,匹配不同設備屏幕尺寸;
常見屏幕適配方式:
布局適配
避免寫死控件尺寸,使用warp_content\match_content;
靈活使用以下屬性設置:
LinearLayout android:layout_weight="0.5"
RelativeLayout android:layout_centerInParent="true"....
ContraintLayout android:layout_constraintLeft_toLeftOf="parent"...
Percent-support-lib xxx:layout_widgetPercent="30%"
圖片資源適配
.9圖或者SVG圖實現縮放
備用位圖匹配不同分辨率
用戶流程適配
根據業務邏輯執行不同的跳轉邏輯
根據別名展示不同的界面
例如:手機和平板適配中就會采用此方式實現
限定符適配
分辨率限定符 drawable-hdpi、drawable-xhdpi,等
尺寸限定符 layout-small(小屏),layout-large(大屏),
最小寬度限定符 values-sw360dp,values-sw384dp,(數字表示設備像素密度)
屏幕方向限定符 layout-land(水平),layout-port(豎直)
市場劉海屏和水滴屏請參考各自官網適配詳情
自定義View適配
原理:以一個特定尺寸設備屏幕分辨率為參考,在View的加載過程中,根據當前設備的實際屏幕分辨率和參考設備屏幕分辨率縮放比換算出View控件的目標像素,再作用到控件上;
實現步驟:
一、創建一個工具類:用來獲取屏幕水平和垂直方向縮放比
public class Utils {
? ? private static Utils instance;
? ? private float mDisplayWidth;
? ? private float mDisplayHeigth;
? ? private float STANDARD_WIDTH = 720;
? ? private float STANDARD_HEIGHT = 1280;
? ? private Utils(Context context) {
? ? ? ? if (mDisplayWidth == 0 || mDisplayHeigth == 0) {
? ? ? ? ? ? // 獲取屏幕實際寬高
? ? ? ? ? ? WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
? ? ? ? ? ? if (manager != null) {
? ? ? ? ? ? ? ? DisplayMetrics metrics = new DisplayMetrics();
? ? ? ? ? ? ? ? manager.getDefaultDisplay().getMetrics(metrics);
? ? ? ? ? ? ? ? if (metrics.widthPixels > metrics.heightPixels) {//橫屏
? ? ? ? ? ? ? ? ? ? mDisplayWidth = metrics.heightPixels;
? ? ? ? ? ? ? ? ? ? mDisplayHeigth = metrics.widthPixels;
? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? mDisplayWidth = metrics.widthPixels;
? ? ? ? ? ? ? ? ? ? mDisplayHeigth = metrics.heightPixels - getStatusBarHeight(context);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? public static Utils getInstance(Context context) {
? ? ? ? if (instance == null) {
? ? ? ? ? ? instance = new Utils(context.getApplicationContext());
? ? ? ? }
? ? ? ? return instance;
? ? }
? ? // 獲取屏幕狀態欄高度
? ? 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 float getHorizontalScale() {
? ? ? ? return mDisplayWidth / STANDARD_WIDTH;
? ? }
? ? // 獲取垂直方向上的縮放比
? ? public float getVerticalScale() {
? ? ? ? return mDisplayHeigth / STANDARD_HEIGHT;
? ? }
}
二、在自定義View中測量時實際使用
public class MyView extends RelativeLayout {
? ? private boolean flag;// 用于標記,防止二次測量
? ? private float scaleX;
? ? private float scaleY;
? ? public MyView(Context context) {
? ? ? ? this(context, null);
? ? }
? ? public MyView(Context context, AttributeSet attrs) {
? ? ? ? this(context, attrs, 0);
? ? }
? ? public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
? ? ? ? super(context, attrs, defStyleAttr);
? ? ? ? //獲取縮放比
? ? ? ? scaleX = Utils.getInstance(getContext()).getHorizontalScale();
? ? ? ? scaleY = Utils.getInstance(getContext()).getVerticalScale();
? ? }
? ? @Override
? ? protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
? ? ? ? if (!flag) {
? ? ? ? ? ? int childCount = getChildCount();
? ? ? ? ? ? if (childCount > 0) {
? ? ? ? ? ? ? ? for (int i = 0; i < childCount; i++) {
? ? ? ? ? ? ? ? ? ? View child = getChildAt(i);
? ? ? ? ? ? ? ? ? ? // 獲取每個View的Params屬性
? ? ? ? ? ? ? ? ? ? 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);
? ? }
}
百分比布局適配
Android提供了Android-percent-support這個庫,支持百分比布局,在一定程度上可以解決屏幕適配的問題
兩種布局:
PercentRelativeLayout和PercentFrameLayout
PercentRelativeLayout繼承RelativeLayout
PercentFrameLayout繼承FrameLayout
官方使用
build.gradle添加:
implementation 'com.android.support:percent:28.0.0'
PercentFrameLayout
<?xml version="1.0" encoding="utf-8"?>
<android.support.percent.PercentFrameLayout
? ? xmlns:android="http://schemas.android.com/apk/res/android"
? ? xmlns:app="http://schemas.android.com/apk/res-auto"
? ? android:layout_width="match_parent"
? ? android:layout_height="match_parent">
? ? <ImageView
? ? ? ? android:layout_width="0dp"
? ? ? ? android:layout_height="0dp"
? ? ? ? app:layout_heightPercent="20%"
? ? ? ? app:layout_widthPercent="50%"
? ? ? ? android:layout_gravity="center"
? ? ? ? android:background="@mipmap/picture"/>
? ? <TextView
? ? ? ? android:layout_width="wrap_content"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? android:textSize="16sp"
? ? ? ? android:layout_gravity="center"
? ? ? ? android:text="孩子"
? ? ? ? android:gravity="center"/>
</android.support.percent.PercentFrameLayout>
根據UI的設計原型在不同的設備屏幕上顯示效果都是一樣的;
下面就是重點了,自己實現谷歌官方百分比布局
一、values文件夾下創建自定義屬性attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
? ? <declare-styleable name="PercentLayout">
? ? ? ? <attr name="widthPercent" format="float"></attr>
? ? ? ? <attr name="heightPercent" format="float"></attr>
? ? ? ? <attr name="marginLeftPercent" format="float"></attr>
? ? ? ? <attr name="marginRightPercent" format="float"></attr>
? ? ? ? <attr name="marginTopPercent" format="float"></attr>
? ? ? ? <attr name="marginBottomPercent" format="float"></attr>
? ? </declare-styleable>
</resources>
二、創建自定義布局,并解析自定義屬性
public class PercentLayout extends RelativeLayout {
public PercentLayout(Context context) {
? ? this(context, null);
}
public PercentLayout(Context context, AttributeSet attrs) {
? ? this(context, attrs, 0);
}
public PercentLayout(Context context, AttributeSet attrs, int defStyleAttr) {
? ? super(context, attrs, defStyleAttr);
}
? ? public static class PercentParams extends RelativeLayout.LayoutParams {
? ? ? ? private float widthPercent;
? ? ? ? private float heightPercent;
? ? ? ? private float marginLeftPercent;
? ? ? ? private float marginRightPercent;
? ? ? ? private float marginTopPercent;
? ? ? ? private float marginBottomPercent;
? ? ? ? public PercentParams(Context c, AttributeSet attrs) {
? ? ? ? ? ? super(c, attrs);
? ? ? ? ? ? // 解析自定義屬性
? ? ? ? ? ? TypedArray array = c.obtainStyledAttributes(attrs, R.styleable.PercentLayout);
? ? ? ? ? ? widthPercent = (float) array.getFloat(R.styleable.PercentLayout_widthPercent, 0);
? ? ? ? ? ? heightPercent = (float) array.getFloat(R.styleable.PercentLayout_heightPercent, 0);
? ? ? ? ? ? marginLeftPercent = (float) array.getFloat(R.styleable.PercentLayout_marginLeftPercent, 0);
? ? ? ? ? ? marginRightPercent = (float) array.getFloat(R.styleable.PercentLayout_marginRightPercent, 0);
? ? ? ? ? ? marginTopPercent = (float) array.getFloat(R.styleable.PercentLayout_marginTopPercent, 0);
? ? ? ? ? ? marginBottomPercent = (float) array.getFloat(R.styleable.PercentLayout_marginBottomPercent, 0);
? ? ? ? ? ? array.recycle();
? ? ? ? }
? ? }
}
三、循環測量子view并設置子view的params值;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
? ? // 獲取父容器寬高
? ? int widthSize = MeasureSpec.getSize(widthMeasureSpec);
? ? int heighSize = MeasureSpec.getSize(heightMeasureSpec);
? ? // 循環遍歷子view并設置params
? ? int childCount = getChildCount();
? ? if (childCount > 0) {
? ? ? ? for (int i = 0; i < childCount; i++) {
? ? ? ? ? ? View child = getChildAt(i);
? ? ? ? ? ? LayoutParams layoutParams = (LayoutParams) child.getLayoutParams();
? ? ? ? ? ? // 如果是自定義的百分比屬性
? ? ? ? ? ? if (checkLayoutParams(layoutParams)) {
? ? ? ? ? ? ? ? PercentParams percentParams = (PercentParams) layoutParams;
? ? ? ? ? ? ? ? float widthPercent = percentParams.widthPercent;
? ? ? ? ? ? ? ? float heightPercent = percentParams.heightPercent;
? ? ? ? ? ? ? ? float marginLeftPercent = percentParams.marginLeftPercent;
? ? ? ? ? ? ? ? float marginRightPercent = percentParams.marginRightPercent;
? ? ? ? ? ? ? ? float marginTopPercent = percentParams.marginTopPercent;
? ? ? ? ? ? ? ? float marginBottomPercent = percentParams.marginBottomPercent;
? ? ? ? ? ? ? ? if (widthPercent > 0) layoutParams.width = (int) (widthSize * widthPercent);
? ? ? ? ? ? ? ? if (heightPercent > 0) layoutParams.height = (int) (heighSize * heightPercent);
? ? ? ? ? ? ? ? if (marginLeftPercent > 0)
? ? ? ? ? ? ? ? ? ? layoutParams.leftMargin = (int) (widthSize * marginLeftPercent);
? ? ? ? ? ? ? ? if (marginRightPercent > 0)
? ? ? ? ? ? ? ? ? ? layoutParams.rightMargin = (int) (widthSize * marginRightPercent);
? ? ? ? ? ? ? ? if (marginTopPercent > 0)
? ? ? ? ? ? ? ? ? ? layoutParams.topMargin = (int) (heighSize * marginTopPercent);
? ? ? ? ? ? ? ? if (marginBottomPercent > 0)
? ? ? ? ? ? ? ? ? ? layoutParams.bottomMargin = (int) (heighSize * marginBottomPercent);
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private boolean checkLayoutParams(LayoutParams params) {
? ? return params instanceof PercentParams;
}
// 這里一定要重寫此方法,并返回自定義的PercentParams
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
? ? return new PercentParams(getContext(), attrs);
}
四、使用自定義百分比布局
<?xml version="1.0" encoding="utf-8"?>
<com.xxx.uidemo.PercentLayout 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:layout_width="wrap_content"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? android:background="@color/colorPrimary"
? ? ? ? android:text="百分比控件"
? ? ? ? app:heightPercent="0.5"
? ? ? ? app:widthPercent="0.5"
? ? ? ? tools:ignore="MissingPrefix" />
</com.xxx.uidemo.PercentLayout>
修改像素密度
修改density(屏幕密度)、scaleDensity(字體縮放比例)、densityDpi(每英寸像素點個數)值-----直接更改系統內部對于目標尺寸而言的像素密度;
原理:布局中不管是dp、sp、pt最終都是通過系統中上面三個參數轉化為相應的px值,只要統一了上面三個值,那么不管什么設備屏幕都能夠適配;
源碼轉化如下:
/frameworks/base/core/java/android/util/TypedValue.java
實現步驟:
一、創建修改Density的工具類
public class Density {
? ? private static final float WIDTH = 320;//參考設備的寬,單位dp;
? ? private static float appDensity;// 表示屏幕密度;
? ? private static float appScaleDensity;// 表示字體縮放比例;默認和appDensity一致
? ? public static void setDensity(final Application application, Activity activity) {
? ? ? ? // 獲取當前app的屏幕顯示信息;
? ? ? ? final DisplayMetrics displayMetrics = application.getResources().getDisplayMetrics();
? ? ? ? if (appDensity == 0 || appScaleDensity == 0) {
? ? ? ? ? ? // 初始化
? ? ? ? ? ? appDensity = displayMetrics.density;
? ? ? ? ? ? appScaleDensity = displayMetrics.scaledDensity;
? ? ? ? ? ? // 當系統字體大小發生變化后,需要重新對appScaleDensity進行賦值;
? ? ? ? ? ? // 監聽系統字體變化回調
? ? ? ? ? ? application.registerComponentCallbacks(new ComponentCallbacks() {
? ? ? ? ? ? ? ? @Override
? ? ? ? ? ? ? ? public void onConfigurationChanged(Configuration configuration) {
? ? ? ? ? ? ? ? ? ? // 字體發生變化后,重新賦值
? ? ? ? ? ? ? ? ? ? if (configuration != null && configuration.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;
? ? }
}
二、在Activity中使用
public class MainActivity extends AppCompatActivity {
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? // 必須在setContentView方法之前執行
? ? ? ? Density.setDensity(getApplication(), this);
? ? ? ? setContentView(R.layout.activity_test);
? ? }
}
布局文件
<?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"
? ? android:layout_width="match_parent"
? ? android:layout_height="match_parent">
? ? <TextView
? ? ? ? android:id="@+id/text"
? ? ? ? android:layout_width="160dp"
? ? ? ? android:layout_height="160dp"
? ? ? ? android:background="@color/colorAccent"
? ? ? ? android:text="Hello World!"
? ? ? ? app:layout_constraintLeft_toLeftOf="parent"
? ? ? ? app:layout_constraintTop_toTopOf="parent" />
? ? <TextView
? ? ? ? android:id="@+id/text1"
? ? ? ? android:layout_width="160dp"
? ? ? ? android:layout_height="160dp"
? ? ? ? android:background="@color/colorAccent"
? ? ? ? android:text="Hello World!"
? ? ? ? app:layout_constraintLeft_toRightOf="@id/text"
? ? ? ? app:layout_constraintTop_toBottomOf="@id/text" />
</android.support.constraint.ConstraintLayout>
三、當有多個界面時的用法
a、抽取基類BaseActivity,在onCreate方法中調用Density.setDensity(getApplication(), this);
b、實現application實現類,在onCreate方法中監聽app所有Activity生命周期并在Activity生命周期onCreate中調用Density.setDensity(getApplication(), this);如下所示
public class App extends Application {
? ? @Override
? ? public void onCreate() {
? ? ? ? super.onCreate();
? ? ? ? registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onActivityCreated(Activity activity, Bundle bundle) {
? ? ? ? ? ? ? ? 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 bundle) {
? ? ? ? ? ? }
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onActivityDestroyed(Activity activity) {
? ? ? ? ? ? }
? ? ? ? });
? ? }
}
劉海屏適配
Android官方9.0劉海屏適配策略
如果時非全屏模式(有狀態欄),則app不受劉海屏影響,劉海屏的高就是狀態欄高度;
全屏模式,app未適配劉海屏,系統會對界面做特殊處理,豎屏下內容區域下移,橫屏下內容區域右移;
所以說適配劉海屏只是在全屏模式下做適配
適配原理:首先app界面設置全屏模式,然后設置內容區域延伸到劉海區;
代碼實現邏輯:
public class TestActivity extends AppCompatActivity {
? ? @Override
? ? protected void onCreate(@Nullable Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? // 1、設置全屏模式
? ? ? ? requestWindowFeature(Window.FEATURE_NO_TITLE);
? ? ? ? Window window = getWindow();
? ? ? ? window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
? ? ? ? // 2、判斷手機是否是劉海屏
? ? ? ? boolean hasDisplayCutout = hasDisplayCutout(window);
? ? ? ? if (hasDisplayCutout) {
? ? ? ? ? ? // 3、設置內容區域延伸至劉海區域
? ? ? ? ? ? WindowManager.LayoutParams layoutParams = window.getAttributes();
? ? ? ? ? ? /*
? ? ? ? ? ? public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT = 0;//默認模式,全屏模式下,內容區域向下移動,非全屏不受影響
? ? ? ? ? ? public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER = 2;// 不管是否全屏,內容區域不能延伸至劉海區
? ? ? ? ? ? public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES = 1;//允許內容區域延伸至劉海區
? ? ? ? ? ? * */
? ? ? ? ? ? layoutParams.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
? ? ? ? ? ? window.setAttributes(layoutParams);
? ? ? ? ? ? // 4、設置沉浸式
? ? ? ? ? ? int flag = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
? ? ? ? ? ? int visibility = window.getDecorView().getSystemUiVisibility();
? ? ? ? ? ? visibility |= flag;
? ? ? ? ? ? window.getDecorView().setSystemUiVisibility(visibility);
? ? ? ? }
? ? ? ? setContentView(R.layout.activity_test);
? ? }
? ? private boolean hasDisplayCutout(Window window) {
? ? ? ? DisplayCutout displayCutout;
? ? ? ? View rootView = window.getDecorView();
? ? ? ? WindowInsets windowInsets = rootView.getRootWindowInsets();
? ? ? ? if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && windowInsets != null) {
? ? ? ? ? ? displayCutout = windowInsets.getDisplayCutout();
? ? ? ? ? ? if (displayCutout != null) {
? ? ? ? ? ? ? ? if (displayCutout.getBoundingRects() != null && displayCutout.getBoundingRects().size() > 0 && displayCutout.getSafeInsetTop() > 0) {
? ? ? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return true;//這里由于使用的是模擬器,暫時設置為true;
? ? }
}
第一次運行未成功,發現原因,需要將AppTheme中添加如下屬性:
<item name="windowNoTitle">true</item>
運行結果截圖:
這里備注一下怎么給模擬器設置劉海屏模式:
開發者選項中設置:
真實開發時處理邏輯:
1、設置全屏模式
2、判斷手機廠商
3、判斷手機是否支持劉海屏
4、設置讓內容區域延伸至劉海屏區域
5、獲取劉海屏高度
6、如果有控件被劉海屏遮擋的情況下,特殊處理,讓此控件向下移動,移動高度就是劉海屏高度;
如遇到如下情況,需要5、6步驟處理,下面的button被遮擋了
此時處理方式:溝通交互設計師,不讓button顯示在這里,或者讓button下移一個劉海屏高度的位置
一般劉海屏高度就是狀態欄高度;
// 獲取屏幕狀態欄高度
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;
}
然后給button設置marginTop屬性或者為button父布局設置paddingTop屬性;
整理了各大廠商劉海屏的適配文檔,請參考
華為:https://devcentertest.huawei.com/consumer/cn/devservice/doc/50114
小米:https://dev.mi.com/console/doc/detail?pId=1341
oppo:https://open.oppomobile.com/service/message/detail?id=61876