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

為什么要屏幕適配?

因?yàn)锳ndroid設(shè)備的碎片化嚴(yán)重厦取,導(dǎo)致app的界面元素在不同分辨率的設(shè)備屏幕尺寸上顯示不一致潮太;

為了讓布局、布局組件虾攻、資源铡买、用戶界面流程,匹配不同設(shè)備屏幕尺寸霎箍;

常見屏幕適配方式:

布局適配

避免寫死控件尺寸奇钞,使用warp_content\match_content;

靈活使用以下屬性設(shè)置:

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圖實(shí)現(xiàn)縮放

備用位圖匹配不同分辨率

用戶流程適配

根據(jù)業(yè)務(wù)邏輯執(zhí)行不同的跳轉(zhuǎn)邏輯

根據(jù)別名展示不同的界面

例如:手機(jī)和平板適配中就會(huì)采用此方式實(shí)現(xiàn)

限定符適配

分辨率限定符 drawable-hdpi、drawable-xhdpi漂坏,等

尺寸限定符 layout-small(小屏)景埃,layout-large(大屏)媒至,

最小寬度限定符 values-sw360dp,values-sw384dp纠亚,(數(shù)字表示設(shè)備像素密度)

屏幕方向限定符 layout-land(水平)塘慕,layout-port(豎直)

市場(chǎng)劉海屏和水滴屏請(qǐng)參考各自官網(wǎng)適配詳情

自定義View適配

原理:以一個(gè)特定尺寸設(shè)備屏幕分辨率為參考,在View的加載過程中蒂胞,根據(jù)當(dāng)前設(shè)備的實(shí)際屏幕分辨率和參考設(shè)備屏幕分辨率縮放比換算出View控件的目標(biāo)像素图呢,再作用到控件上;

實(shí)現(xiàn)步驟:

一骗随、創(chuàng)建一個(gè)工具類:用來獲取屏幕水平和垂直方向縮放比

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) {

? ? ? ? ? ? // 獲取屏幕實(shí)際寬高

? ? ? ? ? ? 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;

? ? }

? ? // 獲取屏幕狀態(tài)欄高度

? ? 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中測(cè)量時(shí)實(shí)際使用

public class MyView extends RelativeLayout {

? ? private boolean flag;// 用于標(biāo)記,防止二次測(cè)量

? ? 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);

? ? ? ? ? ? ? ? ? ? // 獲取每個(gè)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這個(gè)庫鸿染,支持百分比布局指蚜,在一定程度上可以解決屏幕適配的問題

兩種布局:

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>

根據(jù)UI的設(shè)計(jì)原型在不同的設(shè)備屏幕上顯示效果都是一樣的;

下面就是重點(diǎn)了涨椒,自己實(shí)現(xiàn)谷歌官方百分比布局

一摊鸡、values文件夾下創(chuàng)建自定義屬性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>

二、創(chuàng)建自定義布局蚕冬,并解析自定義屬性

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();

? ? ? ? }

? ? }

}

三免猾、循環(huán)測(cè)量子view并設(shè)置子view的params值;

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

? ? // 獲取父容器寬高

? ? int widthSize = MeasureSpec.getSize(widthMeasureSpec);

? ? int heighSize = MeasureSpec.getSize(heightMeasureSpec);

? ? // 循環(huán)遍歷子view并設(shè)置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(每英寸像素點(diǎn)個(gè)數(shù))值-----直接更改系統(tǒng)內(nèi)部對(duì)于目標(biāo)尺寸而言的像素密度锨苏;

原理:布局中不管是dp、sp、pt最終都是通過系統(tǒng)中上面三個(gè)參數(shù)轉(zhuǎn)化為相應(yīng)的px值,只要統(tǒng)一了上面三個(gè)值之景,那么不管什么設(shè)備屏幕都能夠適配;

源碼轉(zhuǎn)化如下:

/frameworks/base/core/java/android/util/TypedValue.java


實(shí)現(xiàn)步驟:

一肯夏、創(chuàng)建修改Density的工具類

public class Density {

? ? private static final float WIDTH = 320;//參考設(shè)備的寬,單位dp犀暑;

? ? private static float appDensity;// 表示屏幕密度驯击;

? ? private static float appScaleDensity;// 表示字體縮放比例;默認(rèn)和appDensity一致

? ? public static void setDensity(final Application application, Activity activity) {

? ? ? ? // 獲取當(dāng)前app的屏幕顯示信息耐亏;

? ? ? ? final DisplayMetrics displayMetrics = application.getResources().getDisplayMetrics();

? ? ? ? if (appDensity == 0 || appScaleDensity == 0) {

? ? ? ? ? ? // 初始化

? ? ? ? ? ? appDensity = displayMetrics.density;

? ? ? ? ? ? appScaleDensity = displayMetrics.scaledDensity;

? ? ? ? ? ? // 當(dāng)系統(tǒng)字體大小發(fā)生變化后徊都,需要重新對(duì)appScaleDensity進(jìn)行賦值;

? ? ? ? ? ? // 監(jiān)聽系統(tǒng)字體變化回調(diào)

? ? ? ? ? ? application.registerComponentCallbacks(new ComponentCallbacks() {

? ? ? ? ? ? ? ? @Override

? ? ? ? ? ? ? ? public void onConfigurationChanged(Configuration configuration) {

? ? ? ? ? ? ? ? ? ? // 字體發(fā)生變化后广辰,重新賦值

? ? ? ? ? ? ? ? ? ? if (configuration != null && configuration.fontScale > 0) {

? ? ? ? ? ? ? ? ? ? ? ? appScaleDensity = application.getResources().getDisplayMetrics().scaledDensity;

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? @Override

? ? ? ? ? ? ? ? public void onLowMemory() {

? ? ? ? ? ? ? ? }

? ? ? ? ? ? });

? ? ? ? }

? ? ? ? // 通過參考設(shè)備的寬暇矫,計(jì)算目標(biāo)值density主之、scaleDensity、densityDpi

? ? ? ? float targetDensity = displayMetrics.widthPixels / WIDTH;// 1080/360 = 3.0

? ? ? ? float targetScaleDensity = targetDensity * (appScaleDensity / appDensity);

? ? ? ? int targetDensityDpi = (int) (targetDensity * 160);

? ? ? ? // 替換當(dāng)前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方法之前執(zhí)行

? ? ? ? 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>

三房轿、當(dāng)有多個(gè)界面時(shí)的用法

a粤攒、抽取基類BaseActivity,在onCreate方法中調(diào)用Density.setDensity(getApplication(), this);

b囱持、實(shí)現(xiàn)application實(shí)現(xiàn)類夯接,在onCreate方法中監(jiān)聽app所有Activity生命周期并在Activity生命周期onCreate中調(diào)用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劉海屏適配策略

如果時(shí)非全屏模式(有狀態(tài)欄),則app不受劉海屏影響纷妆,劉海屏的高就是狀態(tài)欄高度盔几;

全屏模式,app未適配劉海屏掩幢,系統(tǒng)會(huì)對(duì)界面做特殊處理逊拍,豎屏下內(nèi)容區(qū)域下移,橫屏下內(nèi)容區(qū)域右移际邻;

所以說適配劉海屏只是在全屏模式下做適配

適配原理:首先app界面設(shè)置全屏模式顺献,然后設(shè)置內(nèi)容區(qū)域延伸到劉海區(qū);

代碼實(shí)現(xiàn)邏輯:

public class TestActivity extends AppCompatActivity {

? ? @Override

? ? protected void onCreate(@Nullable Bundle savedInstanceState) {

? ? ? ? super.onCreate(savedInstanceState);

? ? ? ? // 1枯怖、設(shè)置全屏模式

? ? ? ? requestWindowFeature(Window.FEATURE_NO_TITLE);

? ? ? ? Window window = getWindow();

? ? ? ? window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

? ? ? ? // 2、判斷手機(jī)是否是劉海屏

? ? ? ? boolean hasDisplayCutout = hasDisplayCutout(window);

? ? ? ? if (hasDisplayCutout) {

? ? ? ? ? ? // 3能曾、設(shè)置內(nèi)容區(qū)域延伸至劉海區(qū)域

? ? ? ? ? ? WindowManager.LayoutParams layoutParams = window.getAttributes();

? ? ? ? ? ? /*

? ? ? ? ? ? public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT = 0;//默認(rèn)模式度硝,全屏模式下,內(nèi)容區(qū)域向下移動(dòng)寿冕,非全屏不受影響

? ? ? ? ? ? public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER = 2;// 不管是否全屏蕊程,內(nèi)容區(qū)域不能延伸至劉海區(qū)

? ? ? ? ? ? public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES = 1;//允許內(nèi)容區(qū)域延伸至劉海區(qū)

? ? ? ? ? ? * */

? ? ? ? ? ? layoutParams.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;

? ? ? ? ? ? window.setAttributes(layoutParams);

? ? ? ? ? ? // 4、設(shè)置沉浸式

? ? ? ? ? ? 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;//這里由于使用的是模擬器驼唱,暫時(shí)設(shè)置為true藻茂;

? ? }

}

第一次運(yùn)行未成功,發(fā)現(xiàn)原因玫恳,需要將AppTheme中添加如下屬性:

<item name="windowNoTitle">true</item>

運(yùn)行結(jié)果截圖:


這里備注一下怎么給模擬器設(shè)置劉海屏模式:

開發(fā)者選項(xiàng)中設(shè)置:


真實(shí)開發(fā)時(shí)處理邏輯:

1辨赐、設(shè)置全屏模式

2、判斷手機(jī)廠商

3京办、判斷手機(jī)是否支持劉海屏

4掀序、設(shè)置讓內(nèi)容區(qū)域延伸至劉海屏區(qū)域

5、獲取劉海屏高度

6惭婿、如果有控件被劉海屏遮擋的情況下不恭,特殊處理叶雹,讓此控件向下移動(dòng),移動(dòng)高度就是劉海屏高度换吧;

如遇到如下情況折晦,需要5、6步驟處理沾瓦,下面的button被遮擋了


此時(shí)處理方式:溝通交互設(shè)計(jì)師满着,不讓button顯示在這里,或者讓button下移一個(gè)劉海屏高度的位置

一般劉海屏高度就是狀態(tài)欄高度暴拄;

// 獲取屏幕狀態(tài)欄高度

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設(shè)置marginTop屬性或者為button父布局設(shè)置paddingTop屬性漓滔;

整理了各大廠商劉海屏的適配文檔,請(qǐng)參考

華為: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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末乖篷,一起剝皮案震驚了整個(gè)濱河市响驴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌撕蔼,老刑警劉巖豁鲤,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異鲸沮,居然都是意外死亡琳骡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門讼溺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來楣号,“玉大人,你說我怎么就攤上這事怒坯§庞” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵剔猿,是天一觀的道長(zhǎng)视译。 經(jīng)常有香客問我,道長(zhǎng)归敬,這世上最難降的妖魔是什么酷含? 我笑而不...
    開封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮汪茧,結(jié)果婚禮上椅亚,老公的妹妹穿的比我還像新娘。我一直安慰自己舱污,他們只是感情好什往,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著慌闭,像睡著了一般别威。 火紅的嫁衣襯著肌膚如雪躯舔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天省古,我揣著相機(jī)與錄音粥庄,去河邊找鬼。 笑死豺妓,一個(gè)胖子當(dāng)著我的面吹牛惜互,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播琳拭,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼训堆,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了白嘁?” 一聲冷哼從身側(cè)響起坑鱼,我...
    開封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎絮缅,沒想到半個(gè)月后鲁沥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡耕魄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年画恰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吸奴。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡允扇,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出则奥,到底是詐尸還是另有隱情考润,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布逞度,位于F島的核電站,受9級(jí)特大地震影響妙啃,放射性物質(zhì)發(fā)生泄漏档泽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一揖赴、第九天 我趴在偏房一處隱蔽的房頂上張望馆匿。 院中可真熱鬧,春花似錦燥滑、人聲如沸渐北。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赃蛛。三九已至恃锉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間呕臂,已是汗流浹背破托。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留歧蒋,地道東北人土砂。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像谜洽,于是被迫代替她去往敵國和親萝映。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容