Android屏幕适配方案(UI稿为iPhone8 plus)

一、UI设计稿尺寸

iPhone设计尺寸参考:https://uiiiuiii.com/screen/ios.htm

在说屏幕适配之前,先提一下UI设计稿的尺寸问题。我们布局时的的尺寸都是通过UI设计图获得,那么UI设计师是以什么屏幕尺寸为标准来设计的就至关重要了。

UI设计稿为Iphone 8 plus(1242px * 2208px)

一些公司IOS和Android共用一套设计稿,假设设计师以IPhone的标准设计,对IOS来说就很友好,但是对Android来说就很坑了。因为Iphone的Retina屏的像素对Android设备来说不是标准的尺寸。

假设UI设计师以iPhone 8 plus的尺寸为标准来设计,iPhone 8 plus的设计尺寸为1242px * 2208px(这里有个坑,iPhone8plus的分辨率为1080 * 1920,和设计稿不一样,需要注意下),在zeplin转化成xxhdpi为414dp*736dp。

现在问题来了,Android从哪找一部手机符合这个尺寸呢?
参考某些手机尺寸信息,发现Nexus6p的分辨率非常接近。

Nexus 6p的分辨率为1440px * 2560px,这是xxxhdpi的尺寸,按照官网的说法,这个屏幕的缩放因子应该为4,可是实际测试scale=3.5。

1440/3.5=411,2560/3.5=731。非常接近设计稿的尺寸!

但是这样是不够精确的,而且还要对Android其他尺寸的设备做适配,如:1080 * 1920,720 * 1280,因此最好是IOS和Android各出一套UI图

UI设计稿尺寸为1080*1920

如果UI以1080 * 1920的尺寸设计,那么就很好了,因为这个尺寸是自带适配属性的。而且国内的大部分手机都是1080的分辨率。

为什么说1080 * 1920是自带适配属性?因为如果你的布局文件用dp做单位,那么下面三种尺寸是完全适配的,因为他们的dp尺寸完全一样。

screenSize = 1440 * 2560 ,  screenDensity = 4, dp = 360 * 640
screenSize = 1080 * 1920 ,  screenDensity = 3, dp = 360 * 640
screenSize = 720 * 1280 ,  screenDensity = 2, dp = 360 * 640

如果出现其他奇葩的屏幕宽度,比如1120、980这种,那么就需要做对应的适配了。

二、屏幕缩放因子

屏幕尺寸计算的关键

Android系统是怎样计算缩放因子的呢?
看看Display.getMetrics()方法做了什么,该方法最终调用下面的方法

 private void getMetricsWithSize(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
            Configuration configuration, int width, int height) {
        //logicalDensityDpi是屏幕固有的dpi
        outMetrics.densityDpi = outMetrics.noncompatDensityDpi = logicalDensityDpi;
        //假设logicalDensityDpi=480,那么outMetrics.density=3,这里DENSITY_DEFAULT_SCALE=1/160=0.00625
        outMetrics.density = outMetrics.noncompatDensity =
                logicalDensityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
        outMetrics.scaledDensity = outMetrics.noncompatScaledDensity = outMetrics.density;
        //下面的x和y方向的dpi是通过dpi的公式计算出来的
        outMetrics.xdpi = outMetrics.noncompatXdpi = physicalXDpi;
        outMetrics.ydpi = outMetrics.noncompatYdpi = physicalYDpi;

        width = configuration != null && configuration.appBounds != null
                ? configuration.appBounds.width() : width;
        height = configuration != null && configuration.appBounds != null
                ? configuration.appBounds.height() : height;

        outMetrics.noncompatWidthPixels  = outMetrics.widthPixels = width;
        outMetrics.noncompatHeightPixels = outMetrics.heightPixels = height;

        if (!compatInfo.equals(CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO)) {
            compatInfo.applyToDisplayMetrics(outMetrics);
        }
    }

屏幕的固有dpi也可以在DisplayMetrics类里面找到

 /**
     * The device's stable density.
     * 

* This value is constant at run time and may not reflect the current * display density. To obtain the current density for a specific display, * use {@link #densityDpi}. */ public static final int DENSITY_DEVICE_STABLE = getDeviceDensity();

因此屏幕的缩放因子是厂商固定好的。而且可能和你根据屏幕尺寸计算的结果不一致。

真机测试

测试机Honor7X

下面是商家给出的手机参数


Android屏幕适配方案(UI稿为iPhone8 plus)_第1张图片
image.png

根据商家给出的尺寸计算dpi = Sqrt(1080^2 + 2160^2)/5.93 = 407.243

在真机上将DisplayMetrics对象打印出来可以看到


Android屏幕适配方案(UI稿为iPhone8 plus)_第2张图片
image.png

屏幕的固有dpi和实际屏幕尺寸计算的dpi差很多,但是Android设备的缩放因子计算却是以固有dpi为准:

缩放因子 = 固有dpi * DisplayMetrics.DENSITY_DEFAULT_SCALE

缩放因子决定屏幕的dp尺寸,而屏幕适配用到的也是dp尺寸。

三、屏幕适配

这里只考虑以宽度为基准适配。适配最终的效果是在不同尺寸的设备上显示相同的UI效果,因此最终显示在各个设备上的尺寸是一个百分比尺寸。

举个例子,假设设计稿为1080px宽度(360dp),Nexus6p手机的宽度为1440px(411dp),那么在1080手机上的1dp,放到1440手机上是多少呢?很简单

width = 1 * 411 / 360 = 1 * (1440 / 3.5) / (1080 / 3) = 1.14 dp

1.生成百分比尺寸

我们在开发时屏幕上的尺寸一般以dp和sp为单位,Android已经帮我们做了一部分的自适应。但是不同屏幕dp的尺寸还是有较大区别。如果以屏幕为基础的百分比尺寸为单位那就肯定不会有问题了。

以生成xxdpi和xdpi文件为例。

以宽度为计算标准,计算百分比尺寸需要几个参数:
1、你现在使用UI设计稿的手机的屏幕宽度。比如我用的Nexus 6p,那么我的宽度为1440。
2、你现在使用UI设计稿的手机的屏幕缩放因子。Nexus 6p为3.5
3、适配手机屏幕的宽度,xxhdpi取1080,xhdpi取720。
4、适配手机屏幕的缩放因子。xxhdpi取3.0,xhdpi取2.0。

xxhdpi的百分比尺寸为:

UI_SCREEN_WIDTH = 1440
UI_SCREEN_SCALE = 3.5
XXHDPI_SCREEN_WIDTH = 1080
XXHDPI_SCREEN_SCALE = 3.0

percentSize = uiSize * ( XXHDPI_SCREEN_WIDTH / XXHDPI_SCREEN_SCALE ) / (  UI_SCREEN_WIDTH / UI_SCREEN_SCALE )

xhdpi的百分比尺寸为:

UI_SCREEN_WIDTH = 1440
UI_SCREEN_SCALE = 3.5
XHDPI_SCREEN_WIDTH = 720
XHDPI_SCREEN_SCALE = 2.0

percentSize = uiSize * ( XHDPI_SCREEN_WIDTH / XHDPI_SCREEN_SCALE ) / ( UI_SCREEN_WIDTH / UI_SCREEN_SCALE)

uiSize就是你使用设计稿的dp尺寸,得出的percentSize就是相对应屏幕的百分比dp尺寸。

上面只适配了1080和720两种宽度,还需要适配哪些宽度呢?
看看友盟的设备统计:https://compass.umeng.com/#/equipment?_k=8snfbu

2.建立适配文件夹

根据自己的适配需求来建立对应的适配文件

适配文件的创建可以参考:Android资源文件说明

继续以Nexus6p为例子,设计稿的尺寸是411dp的,假设我想适配1440(360dp)、1080(360dp)、720(360dp)的标准屏幕,那么我需要建立如下几个文件:

res/values-xhdpi/dimens.xml
res/values-xxhdpi/dimens.xml
res/values-sw411dp-xxxhdpi/dimens.xml
res/values-xxxhdpi/dimens.xml

然后将算好的百分比尺寸填写到这几个文件中,在项目布局文件中直接引用就可以了。

注意,这里411dp的尺寸文件是原始的设计稿尺寸,其他xdpi、xxdpi、xxxdpi都是加了百分比的尺寸。

百分比尺寸文件手写是不可能的,因此我们借助一些其他手段自动生成。

3.新项目dimens文件生成

如果我们的项目刚开始,那么最好先准备好这些文件。

生成工具:dimens文件生成脚本

下面是我生成的三种dpi的dimens.xml文件:


Android屏幕适配方案(UI稿为iPhone8 plus)_第3张图片
image.png

4.老项目dimens文件生成和修改

如果你像我一样项目写到一半发现有适配的问题,那么可以用下面的解决方法。下面可以用到上一步生成的固定dp和sp尺寸文件。

没有适配的项目存在一个默认的res/value/dimens.xml文件,这里的尺寸是默认尺寸,我的项目默认尺寸是针对xxxhdpi屏幕的。还缺少xxhdpi和xhdpi屏幕的dimens.xml文件,xxxhdpi的dimens.xml文件最好也创建一份。

可能想到方法是将现有的dimens文件copy两份,然后修改里面的值为对应的百分比屏幕尺寸。如果现有的dimens文件内容非常多,那么这个工作量将会非常大。下面我们编写脚本自动完成这个过程:


Android屏幕适配方案(UI稿为iPhone8 plus)_第4张图片
image.png

上面三个transfer文件是根据values/dimens.xml文件生成。
自动化脚本:
https://github.com/xionghaoo/Android-screen-adaptation/blob/master/dimen_transfer.py

dimen文件的问题解决了,我们已经写在布局文件中的dp和sp怎么办?下面给出自动替换脚本:

Android屏幕适配方案(UI稿为iPhone8 plus)_第5张图片
image.png

自动化脚本:
https://github.com/xionghaoo/Android-screen-adaptation/blob/master/dimen_modify.py
例如:脚本自动将layout文件夹下面的xml文件中的10dp替换成@dimen/x10dp
注意:程序是根据字符串搜索替换,如果替换出现问题将无法恢复,建议在替换之前先做好备份

上述脚本会自动替换已经写好数值的dp和sp值:


Android屏幕适配方案(UI稿为iPhone8 plus)_第6张图片
image.png

但是TextView的默认字体大小是14sp,也是没有经过适配的,上述脚本也不会自动替换。这时候我们可以在style文件的基础主题里面修改。


Android屏幕适配方案(UI稿为iPhone8 plus)_第7张图片
image.png

到此为止,屏幕适配就完成了。另外还有在代码中写死的尺寸就需要自己手动修改了,不过我相信这种尺寸不会太多。
写死的尺寸.png

替换后的适配尺寸.png

其他

Android的适配以我目前的认知来看并不能做到覆盖所有机型,只能根据自身的需求适配主流机型或一些特定的机型。

另外有一种修改系统固有缩放因子的方案,这种方案会修改系统组件的UI样式,如果有用到系统控件的项目不推荐使用。

屏幕适配github

你可能感兴趣的:(Android屏幕适配方案(UI稿为iPhone8 plus))