Android App不跟随系统显示大小变化

背景

mate40pro用户反馈设置显示大小为最大后,App登录页面异常。Android很多机型除了支持设置字体大小,还可以支持设置“显示大小”。显示大小是7.0及以上系统才支持的功能。设置显示大小,其实最终改变的就是dpi (dots per inch 即每英寸像素数),而dp与px之间的一个很重要的公式:1dp = 1px * (dpi / 160) ,就是根据dpi算出来的。当显示大小调大的时候dpi也会变大,所以最终每1dp占有的像素数就会变大,而当系统分辨率不变的其情况下,对应的dp值就会占用比较大的UI空间(宽度/高度),所以相应的UI显示会变大。

问题

当用户调整系统显示大小时,我们App的某些页面难免会出现一些布局错乱问题。这个时候,最简单粗暴的方式就是设置当前页面不跟随系统显示大小变化(前提是产品同意😂)。设置的方法也比较多,以下是参考网上代码,具体链接见文末:

public static void setDefaultDisplay(Context context) {
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
            Configuration origConfig = context.getResources().getConfiguration();
            //获取手机出厂时默认的densityDpi
            origConfig.densityDpi = getDefaultDisplayDensity();
            context.getResources().updateConfiguration(origConfig, context.getResources().getDisplayMetrics());
        }
    }
 
    public static int getDefaultDisplayDensity() {
        try {
            Class clazz = Class.forName("android.view.WindowManagerGlobal");
            Method method = clazz.getMethod("getWindowManagerService");
            method.setAccessible(true);
            Object iwm = method.invoke(clazz);
            Method getInitialDisplayDensity = iwm.getClass().getMethod("getInitialDisplayDensity", int.class);
            getInitialDisplayDensity.setAccessible(true);
            Object densityDpi = getInitialDisplayDensity.invoke(iwm, Display.DEFAULT_DISPLAY);
            return (int)densityDpi;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

验证

oppp reno验证正常,但mate40pro还是异常,而且设置成默认显示大小也出现异常。通过日志发现,华为mate40pro机型getDefaultDisplayDensity返回的densityDpi是560,而其系统设置默认显示大小时densityDpi却是480,而oppo reno都是480。因此,通过反射getInitialDisplayDensity的方式行不通,会存在兼容性问题。

解决方案

通过翻阅资料,找到这样一段描述:DisplayMetrics.DENSITY_DEVICE_STABLE这个值,无论如何修改显示大小,这个值都是不会发生变化的,都等于用户手机默认的显示大小所对应的densityDpi,因此如果为了临时解决用户修改显示大小而出现的布局错乱问题,可以临时通过这种方式对显示大小进行还原。如果时间充足和项目结构支持的话,还是推荐使用头条适配方案或者其他比较成熟的屏幕适配方案来彻底解决这种UI的适配问题。
经过验证:oppo reno,oppo find x3,vivo x60,vixi x27,华为mate40pro,华为mate20pro,小米9se等机型的DENSITY_DEVICE_STABLE与默认显示大小对应的densityDpi一致,暂未发现不一致的机型,因此先用DENSITY_DEVICE_STABLE作为默认显示大小,以实现当前页面不跟随系统显示大小变化。

public static void setDefaultDisplay(Context context) {
      if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
            Resources res = getResources();
            Configuration configuration = res.getConfiguration();
            if (res.getDisplayMetrics().densityDpi != DisplayMetrics.DENSITY_DEVICE_STABLE{
               configuration.densityDpi = DisplayMetrics.DENSITY_DEVICE_STABLE;
               res.updateConfiguration(configuration, res.getDisplayMetrics());
             }
        }
  }

参考链接

https://juejin.cn/post/6844904045753139207
https://zhongyao.blog.csdn.net/article/details/100695995

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容