[Android folding screen adaptation] Adapt to folding screen based on AutoSize framework and compatible with multi-window mode

[Android folding screen adaptation] Adapt to folding screen based on AutoSize framework and compatible with multi-window mode

problem background

Currently, the latest Android API 33 has relatively complete support for large-screen devices. Combined with responsive layouts such as Jetpack Compose, it can be adapted to all screens at once. However, considering that the code often cannot be refactored immediately, if you want to use the stock code to adapt to the endless large screen and foldable devices, and to be compatible with multi-window scenarios, you can use the well-known AutoSize framework in the Android world to cooperate with 2 sets of design drawing sizes to complete. .

Fundamentals of the AutoSize framework

The AutoSize framework supports 5 types of units, including dp, sp, pt, in, mm, among which dp and sp are the main units of the AutoSize framework. During the development process, the support for a certain unit can be turned on or off, and it is possible to use the main unit + sub-unit or only the sub-unit for adaptation. Whichever sub-unit you choose, you can use that unit when writing the layout.
The core principle of AutoSize adapting the screen is to obtain the screen width and height (unit is px) in DisplayMetrics through WindowManager, and then combine the set design width and height (unit is px) to calculate and set the density globally (for details, please refer to the source code of the AutoSize framework ) .

Set in Application and BaseActivity

Considering that the folding screen has 2 sets of different screen widths and heights when opening and closing, we set 2 sets of design widths and heights for it to correctly calculate the density. Since there are two sets of design drawing sizes, it is not feasible to set the width and height of the design drawing in AndroidManifest. It is necessary to calculate the current screen aspect ratio and window aspect ratio in Application onCreate() and manually set the design drawing width and height. The code is as follows.
In addition, in the scene of screen folding and rotation, the screen width and height obtained from the onConfigurationChanged() callback of registerComponentCallbacks are sometimes inaccurate, and the reason has not been located yet (the increase in delay is also unstable), so it needs to be in onConfigurationChanged of the encapsulated BaseActivity () The following processing is also added to the life cycle.

        /*注册屏幕适配监听器,适配前后均会回调此方法*/
        AutoSizeConfig.getInstance().setOnAdaptListener(new onAdaptListener() {
    
    
            @Override
            public void onAdaptBefore(Object target, Activity activity) {
    
    
                if (activity == null) {
    
    
                    return;
                }

                int[] windowSize = new int[2];
                windowSize[0] = activity.getResources().getDisplayMetrics().widthPixels;
                windowSize[1] = activity.getResources().getDisplayMetrics().heightPixels;

                AutoSizeConfig.getInstance()
                        .setCustomFragment(true)
                        .getUnitsManager()
                        .setSupportSP(false)
                        .setSupportDP(false)
                        .setSupportSubunits(Subunits.MM);

                /*判断屏幕宽高比是否为大屏*/
                if (FoldScreenUtil.isLargeScreen(activity)) {
    
    
                    /*判断window宽高比是否为大窗口*/
                    if (FoldScreenUtil.isLargeWindow(activity)) {
    
    
                        Log.i("isLargeScreen", "isLargeWindow");
                        /*判断横竖屏*/
                        if (activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
    
    
                            /*大屏-大窗口-横屏设计尺寸*/
                            AutoSizeConfig.getInstance()
                                    .getUnitsManager()
                                    .setDesignWidth(windowSize[0])
                                    .setDesignHeight(windowSize[1])
                                    .setDesignSize(1556, 1395);
                        } else {
    
    
                            /*大屏-大窗口-竖屏设计尺寸*/
                            AutoSizeConfig.getInstance()
                                    .getUnitsManager()
                                    .setDesignWidth(windowSize[0])
                                    .setDesignHeight(windowSize[1])
                                    .setDesignSize(1395, 1556);
                        }
                    } else {
    
    
                        if (activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
    
    
                            /*大屏-小窗口-横屏设计尺寸*/
                            AutoSizeConfig.getInstance()
                                    .getUnitsManager()
                                    .setDesignWidth(windowSize[0])
                                    .setDesignHeight(windowSize[1])
                                    .setDesignSize(1556, 750);
                        } else {
    
    
                            /*大屏-小窗口-竖屏设计尺寸*/
                            AutoSizeConfig.getInstance()
                                    .getUnitsManager()
                                    .setDesignWidth(windowSize[0])
                                    .setDesignHeight(windowSize[1])
                                    .setDesignSize(750, 1556);
                        }
                    }
                } else {
    
    
                    /*小屏通常禁止旋转,仅设置竖屏设计尺寸即可*/
                    /*特殊情况:平行视窗下只能拿到当前activity的显示区域宽高,会被判定为小屏*/
                    AutoSizeConfig.getInstance()
                            .getUnitsManager()
                            .setDesignWidth(windowSize[0])
                            .setDesignHeight(windowSize[1])
                            .setDesignSize(750, 1556);
                }
            }
            @Override
            public void onAdaptAfter(Object target, Activity activity) {
    
    
            }
        });

Note: The Activity window is not equivalent to the screen in a multi-window scenario

In multi-window scenarios (such as parallel windows, split screens, and floating windows, etc.), the window size is not equivalent to the screen size. If the screen width and height are still adapted in multi-window mode, the density must be Incorrect value. However, the width and height obtained in the AutoSize framework are the screen width and height, so you need to use the Activity context to get the window size and set it manually.
After testing, parallel windows are special, and the obtained screen width and height are the width and height of the area displayed by the current activity.
You can refer to the following methods to determine whether the current window is in the large window state.

    public static boolean isLargeWindow(Activity activity) {
    
    
        if (activity == null) {
    
    
            return false;
        }
        float longSide = 0;
        float shortSide = 0;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
    
    
            WindowMetrics windowMetrics = activity.getWindowManager().getCurrentWindowMetrics();
            Rect windowRect = windowMetrics.getBounds();
            longSide = Math.max(windowRect.width(), windowRect.height());
            shortSide = Math.min(windowRect.width(), windowRect.height());
        } else {
    
    
            int widthPixels = activity.getResources().getDisplayMetrics().widthPixels;
            int heightPixels = activity.getResources().getDisplayMetrics().heightPixels;
            longSide = Math.max(widthPixels, heightPixels);
            shortSide = Math.min(widthPixels, heightPixels);
        }

        boolean isLargeWindow = longSide / shortSide < MIN_ASPECT;
        Log.i("isLargeWindow", "longSide" + longSide + "shortSide" + shortSide);
        return isLargeWindow;
    }

Set it in item's getView() or onBindViewHolder

Due to the item multiplexing mechanism of components such as GridView and RecyclerView, the width and height of items may be incorrect when the screen is opened, closed or rotated. Adding AutoSize.autoConvertDensityOfGlobal(activity) to getView() to manually set the density can solve this problem.

Guess you like

Origin blog.csdn.net/weixin_38702864/article/details/127071799