android 最全屏幕適配方案-自定義實現谷歌百分比布局(包含一些大廠劉海屏適配)

為什么要屏幕適配?

因為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

Vivo:https://dev.vivo.com.cn/documentCenter/doc/103

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,327評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,996評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,316評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,406評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,128評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,524評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,576評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,759評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,310評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,065評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,249評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,821評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,479評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,909評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,140評論 1 290
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,984評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,228評論 2 375

推薦閱讀更多精彩內容