android中的
传统dp适配方式的缺点
dp在渲染之前会将dp转换为px。计算公式为:
px=密度* dp;
密度=dpi/160;
px=dp * (dpi/160);
DPI是根据屏幕的实际分辨率和尺寸计算的,每个设备可能会有所不同。
屏幕尺寸、分辨率和像素密度之间的关系
通常,手机的分辨率为宽x高,屏幕尺寸的单位为英寸。三者之间的关系是:
例如:屏幕分辨率为:1920*1080,屏幕尺寸为5英寸,则dpi为440。
这有什么问题吗?
假设我们的UI设计图是按照屏幕宽度360dp来设计的,那么在上述设备上,屏幕宽度实际上是1080/(440/160)=392.7dp,也就是说屏幕比设计图宽。这种情况下,即使使用dp,在不同的设备上也无法显示相同的效果。同时,也存在一些设备的屏幕宽度小于360dp的情况,这会导致按照360dp宽度开发时显示不完整。
而且,很多设备并没有按照这个规则来实现上述屏幕尺寸、分辨率和像素密度之间的关系。所以dpi值很乱,没有规律可循,导致dp适配效果不理想。
探索新的适配方式
梳理要求
首先,我们先来梳理一下我们的需求。一般我们的设计图纸都是按照固定尺寸来设计的。例如,如果您使用1920px * 1080px 的分辨率进行设计,并以密度标记为3,则意味着屏幕实际上是640dp * 360dp。如果我们希望所有设备上的显示完全一致,其实是不现实的,因为屏幕长宽比并不是固定的。 16:9、4:3,甚至其他长宽比层出不穷。不同的长宽比,不可能呈现出完全一致的显示效果。但通常,我们只需要在一维上进行调整:宽度或高度。例如,如果我们的feed 上下滑动,我们只需要保证它在所有设备上在宽度维度上显示一致即可。再比如不支持上下滑动的页面。那么就需要保证在这个高维度下显示是一致的,特别是在某些设备上不会出现显示不完整的情况。同时考虑到适配基本都是以dp为单位进行的,如果新方案不支持dp,那么迁移成本将会非常高。
因此,总体要求总结如下:
支持一维适配:宽度或高度,保持该尺寸与设计一致;
支持dp和sp单位,将迁移成本控制在最低。
寻找兼容性突破
dp 和px 的换算公式:px=dp * 密度
可见,如果设计宽度为360dp,我们要保证在所有设备上计算出的px值正好是屏幕宽度,那么我们只能修改密度值。
通过阅读源码我们可以知道,density是DisplayMetrics中的一个成员变量,通过Resources#getDisplayMetrics可以获取DisplayMetrics实例,而Resouces则可以通过Activity或Application的Context获取。
首先我们先熟悉一下DisplayMetrics中与适配相关的几个变量:
DisplayMetrics#密度就是上面提到的密度
DisplayMetrics#密度Dpi就是上面的dpi
DisplayMetrics#scaledDensity 字体的缩放因子。通常它等于密度,但是调整系统字体大小会改变这个值。
那么所有的dp和px转换都是通过DisplayMetrics中的相关值计算出来的吗?
首先我们看一下布局文件中dp的转换。最后调用TypedValue#applyDimension(intunit,floatvalue,DisplayMetricsmetrics)进行转换:
这里使用的DisplayMetrics 是从Resources 中获取的。
我们再看一下图像的解码,BitmapFactory#decodeResourceStream方法:
可见性也是根据DisplayMetrics 中的值计算的。
当然,还有其他dp转换场景,基本都是通过DisplayMetrics计算的,这里不再详述。因此,要满足上述要求,我们只需要修改DisplayMetrics中与dp转换相关的变量即可。
最终计划
下面假设设计图的宽度为360dp,并适配宽度尺寸。
那么适配后的密度=设备的真实宽度(单位px)/360。接下来我们只需要修改我们在系统中计算的密度即可。代码实现如下:
同时在Activity#onCreate方法中调用。代码比较简单,不涉及调用系统非公开的API,所以理论上不会影响app的稳定性。
所以修改之后,推出了灰度测试的版本。稳定性符合预期。没有因此导致崩溃,但是我们收到很多反馈说字体太小:
原因是在上面的适配中,我们忽略了DisplayMetrics#scaledDensity 的特殊性,将DisplayMetrics#scaledDensity 和DisplayMetrics#density 设置为相同的值。结果有用户在系统中修改了字体大小,失败了,但是我们原来的scaledDensity也不能直接使用。如果直接使用,有些文字可能会超出显示区域。因此,我们可以通过计算之前的scaledDensity与密度的比值来得到当前的scaledDensity,如下:
不过测试后又发现了一个问题,那就是如果在系统设置中切换字体,然后返回应用程序,字体并没有改变。所以要监听字体切换,只需调用Application#registerComponentCallbacks注册onConfigurationChanged来监听即可。
所以最终的解决方案如下:
当然,上面的代码只适配了360dp的设计宽度。如果想适应高维度,可以扩展代码。
Showcase
适配前后设计图对比:
各模型适配后的显示效果:
本文采摘于网络,不代表本站立场,转载联系作者并注明出处:https://www.iotsj.com//kuaixun/5798.html
用户评论
终于不用担心不同手机分辨率了!这种方法真是太棒了
有12位网友表示赞同!
极低成本?这让我更加好奇它的原理是什么!
有7位网友表示赞同!
分享一下详细的操作步骤呗,我也想试试这个适配方式
有14位网友表示赞同!
Android屏幕适配一直是老问题了,期待这种新方式能解决这个问题
有13位网友表示赞同!
有没有什么缺点呢?总觉得太完美了反而令人担忧
有8位网友表示赞同!
如果真的能做到极低成本,那开发效率肯定会大幅提升
有13位网友表示赞同!
让我来试试这个方案!希望效果能像文章描述的那么好
有7位网友表示赞同!
这种方法是不是只能适配Android手机?期待更多平台的支持
有5位网友表示赞同!
终于不用再折腾复杂的布局代码了,太省心啦!
有18位网友表示赞同!
希望能详细说明一下如何实现"极低成本"的效果
有8位网友表示赞同!
这篇文章提到的技巧我之前也尝试过,效果确实不错!
有13位网友表示赞同!
这种方法对程序的运行速度有什么影响呢?
有19位网友表示赞同!
听起来很新颖,期待看到更多针对这个适配方式的应用案例!
有15位网友表示赞同!
感觉这个方法可以应用到很多类型APP中去
有15位网友表示赞同!
学习一下这方面的新技术,提升自己的开发能力
有18位网友表示赞同!
希望这种低成本适配方式能够被广泛应用
有8位网友表示赞同!
这款Android屏幕适配方法是不是简单易用?
有6位网友表示赞同!
我试过很多屏幕适配方案,都感觉效果一般般,期待这个方法能带来突破
有20位网友表示赞同!