一种非常简单的Android屏幕适配方案

news/2024/12/28 20:00:15/

转载请作明出处:https://blog.csdn.net/Raul575_Li/article/details/80801312
##前言
作为一个Android开发人员,你还在为了适配各种尺寸的屏幕而苦恼吗?你还在为了出现一个新的机型而修改着数不尽的dimens和layout吗?你还在为了UI给的奇葩尺寸的设计图而绞尽奶汁计算距离吗?如果你为了这些事情而苦恼,那么看完这篇文章,希望可以帮你减少开发时间,减缓生命的流逝速度。。。

不知道大家有没有看过前一段时间今日头条技术团队发表的一篇关于Android屏幕适配的文章:一种极低成本的Android屏幕适配方式。没有看过的朋友可以先看看了解一下再回来,可以更好的理解。我是无意中点开的这篇文章,但是看过之后眼前一亮-------Android屏幕适配要是真的这么简单,那些辛辛苦苦没日没夜做适配的前辈们是不是死得太惨了。。。。。。。

不比比太多,开搞!
##测试与思考
不得不说今日头条的大神们的想法真的非常独到,成本极其低廉,还特别好用。他们给出的最终方案是这样的:

private static float sRoncompatDennsity;
private static float sRoncompatScaledDensity;private void setCustomDensity(@NonNull Activity activity, final @NonNull Application application) {//applicationfinal DisplayMetrics appDisplayMetrics = application.getResources().getDisplayMetrics();if (sRoncompatDennsity == 0) {sRoncompatDennsity = appDisplayMetrics.density;sRoncompatScaledDensity = appDisplayMetrics.scaledDensity;application.registerComponentCallbacks(new ComponentCallbacks() {Overridepublic void onConfigurationChanged(Configuration newConfig) {if (newConfig != null && newConfig.fontScale > 0) {sRoncompatScaledDensity = application.getResources().getDisplayMetrics().scaledDensity;}}@Overridepublic void onLowMemory() {}});}//计算宽为360dp 同理可以设置高为640dp的根据实际情况final float targetDensity = appDisplayMetrics.widthPixels / 360;final float targetScaledDensity = targetDensity * (sRoncompatScaledDensity / sRoncompatDennsity);final int targetDensityDpi = (int) (targetDensity * 160);appDisplayMetrics.density = targetDensity;appDisplayMetrics.densityDpi = targetDensityDpi;appDisplayMetrics.scaledDensity = targetScaledDensity;//activityfinal DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();activityDisplayMetrics.density = targetDensity;activityDisplayMetrics.densityDpi = targetDensityDpi;activityDisplayMetrics.scaledDensity = targetScaledDensity;
}

看到这篇文章之后我赶紧就写了一个demo测试了一下,发现了一点小问题。
我们UI给出的设计图尺寸为1334*720,如果我按照宽度作为适配标准的话,按照设计图720px的宽度,屏幕的宽度应为360dp,也就是这样:

final float targetDensity = appDisplayMetrics.widthPixels / 360;

这样做的话宽度适配的比例是没有任何问的,但是我在想,如果某一个页面需要以高度来做适配(也就是内容刚好纵向填充全屏)的话,是不是改成这样就可以了:

final float targetDensity = appDisplayMetrics.heightPixels / 667;

但是运行之后发现,高度上的差异很大,运行在不同分辨率和尺寸的手机上,页面中的每一部分内容在纵向上的比例不尽相同,没有达到很好的适配的效果。
思考了许久过后我发现一个问题:我手边的测试机的宽度是两个720和两个1080,而高度有1280,1440,1780和一个全面屏的2160。Android的开原性导致了Android设备的尺寸的碎片化太严重,而通过查看测试机的尺寸参数会发现,如果用这四个手机来测试的话,宽度可以直接整除,而高度不可以(并且我手边的测试机的宽度也可以整除,如果有宽度没法整除的手机呢?)。但是用今日头条给出的方法,做除法后结果会取整,那会不会是由于用纵向计算出来的density取整影响了精度,从而导致了效果不尽人意呢?

来,继续改
##问题修复
发现上述问题之后我就着手去修改,将计算结果取余后在赋值给targetDensity,经过反复的测试与实验,我重新修改了targetDensity的计算方法:

float targetDensity = 0;
try {//appDisplayMetrics.heightPixels/667Double division = Operation.division(appDisplayMetrics.heightPixels, 667);//由于手机的长宽不尽相同,肯定会有除不尽的情况,有失精度,所以在这里把所得结果做了一个保留两位小数的操作DecimalFormat df = new DecimalFormat("0.00");String s = df.format(division);targetDensity = Float.parseFloat(s);
} catch (NumberFormatException e) {e.printStackTrace();
}

但是有热心的网友给我留言,说如果把系统的语言改成葡萄牙语之后会报异常,页面完全就是惨不忍睹。。。抱着对外国历史友人的疑问我继续去测试,发现了这样的问题:

仔细看!!!小数点竟然是中文逗号(目前发现的语言当中,葡语和印尼语是这样,其他语言未经测试)!!!没办法,只能继续修改了,后来经过这位热心网友提醒,我将这部分代码修改成:

float targetDensity = appDisplayMetrics.heightPixels / 667f;

这样一来就完全没有问题了,也不需要做保留两位小数处理了(发现问题的朋友们,原谅我学艺不精。。。也感谢这位热心网友的指正)

继续测试后发现,高度上的适配结果让人非常满意。可是还有一个问题,我们一般来说做适配都是以手机的宽度为基准,但是一个app里面避免不了偶尔一两个页面是按照高度为基准(就是内容纵向填充全屏的页面)做适配的。但是上述方法只能保证一个方向,那我就让它可以自由的切换适配的基准方向不就好了。
##最终方案
继续修改之后我得到了最终的方案,修改过后这个类中的所有内容如下:

private static float appDensity;
private static float appScaledDensity;
private static DisplayMetrics appDisplayMetrics;
private static int barHeight;public static void setDensity(@NonNull Application application) {//获取application的DisplayMetricsappDisplayMetrics = application.getResources().getDisplayMetrics();//获取状态栏高度barHeight = AppUtils.getStatusBarHeight(application);if (appDensity == 0) {//初始化的时候赋值appDensity = appDisplayMetrics.density;appScaledDensity = appDisplayMetrics.scaledDensity;//添加字体变化的监听application.registerComponentCallbacks(new ComponentCallbacks() {@Overridepublic void onConfigurationChanged(Configuration newConfig) {//字体改变后,将appScaledDensity重新赋值if (newConfig != null && newConfig.fontScale > 0) {appScaledDensity = application.getResources().getDisplayMetrics().scaledDensity;}}@Overridepublic void onLowMemory() {}});}
}//此方法在BaseActivity中做初始化(如果不封装BaseActivity的话,直接用下面那个方法就好了)
public static void setDefault(Activity activity) {setAppOrientation(activity, AppUtils.WIDTH);
}//此方法用于在某一个Activity里面更改适配的方向
public static void setOrientation(Activity activity, String orientation) {setAppOrientation(activity, orientation);
}/*** targetDensity* targetScaledDensity* targetDensityDpi* 这三个参数是统一修改过后的值* <p>* orientation:方向值,传入width或height*/
private static void setAppOrientation(@Nullable Activity activity, String orientation) {float targetDensity;if (orientation.equals("height")) {targetDensity = (appDisplayMetrics.heightPixels - barHeight) / 667f;} else {targetDensity = appDisplayMetrics.widthPixels / 360f;}float targetScaledDensity = targetDensity * (appScaledDensity / appDensity);int targetDensityDpi = (int) (160 * targetDensity);/**** 最后在这里将修改过后的值赋给系统参数** 只修改Activity的density值*/DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();activityDisplayMetrics.density = targetDensity;activityDisplayMetrics.scaledDensity = targetScaledDensity;activityDisplayMetrics.densityDpi = targetDensityDpi;
}

在这个类的初始化方法里面我是默认的以宽度来作为基准(这是在Activity中设置的方法,存在于此Activity下的fragment,dialog和PopupWindow都会受到此效果的影响,也就是说,在Activity中设置一次之后,Activity下的其他子View都无需再设置一次)。
##使用方法
自己创建一个类,将最终方案里面的代码复制粘贴就可以使用了

使用方法:在Application的onCreate()方法中:

在BaseActivity中:

如果只是适配一个方向的话,只设置这两句就可以了(我在utils里面设置了默认按照宽度适配,可以根据自己的需求修改默认的适配方向,见下图)

若app中有某一个页面需要纵向适配的话(注意代码中的注释):

/**** 由于是个人封装,此方法需要写在onCreate()中的setContentView()方法前面,切换方向的效果才会生效*/
@Override
public void setOrientation() {Density.setOrientation(this, AppUtils.HEIGHT);
}


最后贴出适配的效果图(颜色只是为了看的直观一点。。。)
横向
纵向
##敲黑板!!!
使用此方法,只需要一个dimens文件,一个layout文件就足矣,在xml布局中直接只用dp就可以了(Android P的刘海屏需要单独适配layout)
##结语
由于是自己写的demo,还没有大面积测试(但是今日头条已经启用了该方案,现在一个多月过去了还没听说出现什么问题,那就说明应该还可以),要是各位看官有条件大范围测试的话,出现什么问题可以反馈给我,我们可以一起讨论该如何修改,共同进步。
这是我入行以来写的第一篇文章,有写的不好的地方欢迎指正,以后还会继续努力多写文章的,好的东西需要分享。

这里是github地址,demo里面还有BaseRecyclerViewAdapterHelper和QMUI的使用方法

这篇文章如果帮到你的话,点个喜欢再走呗


http://www.ppmy.cn/news/881697.html

相关文章

Arduino+esp8266+1.4寸TFT屏(st7735驱动)解决显示图片偏色以及屏幕边缘花边问题

买回来的屏幕一跑例程出现这个情况&#xff1f; 原图&#xff1a; 屏幕显示&#xff1a; 首先偏色问题存在&#xff0c;且屏幕花边&#xff08;没复现的出来&#xff0c;假装有&#xff09;。 解决方法&#xff1a; 主要问题出现在 Arduino\libraries\TFT_eSPI 这个库下的…

Android开发——Android手机屏幕适配方案总结

0. 前言 Android的屏幕适配&#xff0c;即使得某一元素在Android不同尺寸、不同分辨率的手机上具备相同的显示效果&#xff0c;这个问题一直以来都是我们Android开发者不得不面对的问题。本文参考了很多前人的博客&#xff0c;并对这一问题做一个总结&#xff0c;力求精简明了…

ESP8266与li9488 3.5寸TFT屏连接显示+触摸

前言 我用的是 NodeMCU1.0 ESP-12E 的8266板子&#xff0c;TFT彩屏用的是3.5寸480*320的ILI9488。 提示&#xff1a;以下我对两个模块连接的经验 一、使用TFT_eSPI库 安装配置方法不再赘述&#xff0c;不懂得可以先学习&#xff0c;不要着急 二、配置内容 1.设备连线 VCC…

使用自己开发的app远程控制MAX7219点阵屏幕

使用自己开发的app远程控制MAX7219点阵屏幕 一.功能介绍&#xff1a;二.芯片介绍&#xff1a;三.实现原理&#xff1a;四.代码部分&#xff1a;其它资料&#xff1a; 一.功能介绍&#xff1a; 1.可以固定显示想显示的内容 2.点阵屏幕在无指令的时候3秒换一次内容 3.可以通过云…

iOS开发 -手机屏幕适配的简单使用

我们先来看一张美工小姐姐提供的蓝湖图。 发现什么了吗&#xff1f; 设计尺寸750X1314是设备(物理)分辨率。其实美工是按照6/6s/7/8标准标注的图。因为6/6s/7/8的缩放因子是2x&#xff0c;所以750/2X1314/2 375X667&#xff0c;就是我们6/6s/7/8手机的逻辑分辨率。 //UI设计基…

数据库系统 - 家庭教育平台设计开发

目录 1.绪论 1.1项目背景 1.2家庭教育平台的发展现状与优势 1.2.1国内外发展现状 1.2.2家庭教育平台的优势 2.需求分析 2.1可行性分析 2.1.1经济可行性 2.1.2 技术可行性 2.1.3操作可行性 2.2系统功能 2.2.1 家庭教育资源 2.2.2 家庭教育指导师 2.2.3家庭教育咨询…

使用 SageMaker 对 Whisper 模型进行微调及部署

使用 SageMaker 对 Whisper 模型进行微调及部署 Whisper 作为 OpenAI 最新开源的自动语音识别&#xff08;ASR&#xff09;模型&#xff0c;采用了编码器-解码器&#xff08;encoder- decoder&#xff09;transformer架构&#xff0c;并使用了 68 万小时的从互联网收集的多语言…

电脑广告弹窗怎么解决?

前几天女朋友下载植物大战僵尸往自己电脑上下载了一堆奇奇怪怪的东西&#xff0c;导致电脑广告弹窗异常的多。于是乎天天跟我嘤嘤嘤。 1、出现广告弹窗以后打开任务管理器&#xff0c;在进程里找到广告的后台进程&#xff0c;右键属性&#xff0c;打开文件的具体位置。&#x…