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.
     * <p>
     * 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

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


image.png

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

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


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文件:


image.png

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

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

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

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


image.png

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

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

image.png

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

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


image.png

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


image.png

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

替换后的适配尺寸.png

其他

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

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

屏幕适配github

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容