美文网首页
刘海屏的适配

刘海屏的适配

作者: 石器时代小古董 | 来源:发表于2019-06-08 19:49 被阅读0次

    一、适配策略

    1. 非全屏状态下不需要适配刘海屏,刘海只占据 toolbar 的位置。
    2. 全屏时需要适配刘海屏

    二、使用系统提供的 CutoutModel

    CutoutModel 有三种模式。
    **1.LAYOUY_IN_DISPLAY_CUTOUT_MODEL_DEFAULT 全屏模式下内容下移,非全屏不受影响 **

    **1.LAYOUY_IN_DISPLAY_CUTOUT_MODEL_SHORT_EDGES 允许内容区延伸进刘海区 **

    **1.LAYOUY_IN_DISPLAY_CUTOUT_MODEL_NEVER 不允许内容区延伸进刘海区 **

     //2.让内容区域延伸进刘海
    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);
    

    三、设置沉浸式布局

    沉浸式的布局才可以让自己的 View 延伸到状态栏中。如果不启用沉浸式的布局顶部会是黑边。

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

    四、判断是否有刘海

    判断是否有刘海可以从是否具备刘海 刘海的高度不为0 版本在9.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){
                    // 判断有几个刘海,刘海数不为 0  且 刘海的高度大于 0
                    if (displayCutout.getBoundingRects() != null && displayCutout.getBoundingRects().size() > 0 && displayCutout.getSafeInsetTop() > 0){
                        return true;
                    }
                }
            }
            return false; 
        }
    

    七、View 在刘海处的解决方案

    1.让这个 View 下移刘海的高度
    2.设置父容器的上部分的 Padding 为刘海的高度

       Button button = findViewById(R.id.button);
            RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) button.getLayoutParams();
            layoutParams.topMargin = heightForDisplayCutout();
            button.setLayoutParams(layoutParams);
    
            RelativeLayout layout = findViewById(R.id.container);
            layout.setPadding(layout.getPaddingLeft(), heightForDisplayCutout(), layout.getPaddingRight(), layout.getPaddingBottom());
    

    六、总结

    刘海屏的适配的流程是

    1.是否有刘海(需要考虑华为,OPPO,小米等厂商的定制)

    2.进行全屏设置

    3.沉浸式设置

    4.如果有 View 在刘海处,单独考虑

    public class DisplayCutoutActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            //1.设置全屏
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            Window window = getWindow();
            window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
    
            //华为, 小米,oppo
            //1.判断手机厂商, 2,判断手机是否刘海, 3,设置是否让内容区域延伸进刘海 4,设置控件是否避开刘海区域  5, 获取刘海的高度
    
            //判断手机是否是刘海屏
            boolean hasDisplayCutout = hasDisplayCutout(window);
            if (hasDisplayCutout){
                //2.让内容区域延伸进刘海
                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);
    
                //3.设置成沉浸式 这样才能真的让自己 View 的内容进入刘海区,否则只能
                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);
            }
    
            setContentView(R.layout.activity_main);
            // 如果某一个控件在刘海位置,可以获取刘海的高度,让它下降这部分高度
    //        Button button = findViewById(R.id.button);
    //        RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) button.getLayoutParams();
    //        layoutParams.topMargin = heightForDisplayCutout();
    //        button.setLayoutParams(layoutParams);
            // 设置 Padding 让布局向下移动
            RelativeLayout layout = findViewById(R.id.container);
            layout.setPadding(layout.getPaddingLeft(), heightForDisplayCutout(), layout.getPaddingRight(), layout.getPaddingBottom());
        }
    
        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){
                    // 判断有几个刘海,刘海数不为 0  且 刘海的高度大于 0
                    if (displayCutout.getBoundingRects() != null && displayCutout.getBoundingRects().size() > 0 && displayCutout.getSafeInsetTop() > 0){
                        return true;
                    }
                }
            }
            return true; //因为模拟器原因,这里设置成true
        }
    
        //通常情况下,刘海的高就是状态栏的高
        // 也可以使用 displayCutout.getSafeInsetTop() 来设置
        public int heightForDisplayCutout(){
            int resID = getResources().getIdentifier("status_bar_height", "dimen", "android");
            if (resID > 0){
                return getResources().getDimensionPixelSize(resID);
            }
            return 96;
        }
    }
    

    华为小米OPPO手机是否有刘海

    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");
            }
        }
    
        /*********
         * 1、声明全屏显示。
         *
         * 2、适配沉浸式状态栏,避免状态栏部分显示应用具体内容。
         *
         * 3、如果应用可横排显示,避免应用两侧的重要内容被遮挡。
         */
    
    
        /********************
         * 判断该 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的刘海高度小于等于状态栏高度
         */
        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;
            }
        }
    
    }
    

    相关文章

      网友评论

          本文标题:刘海屏的适配

          本文链接:https://www.haomeiwen.com/subject/ldqwxctx.html