Android 屏幕适配初探

1,105 阅读5分钟

一. 屏幕适配背景

众所周知,前端工程师(UI切图仔)的界面开发工作离不开设计师精心制作标记的视觉稿。一顿操作后终将页面还原到了移动端上,可往往拿给视觉走查效果时,收获的不是赞赏,而是“到底有没有按标注来呀?”!

image.png

本着程序员的自我修养: 说我可以,说我的代码不行。夺过视觉的手机飞奔回工位,奇怪,明明就是按照视觉稿来处理的布局,怎么到手机上展示就不对劲了呢。

image.png

这时我们就不得不提安卓市场五花八门五光十色的机器设备了。

相比iPhone,安卓的发展可谓是百花齐放,基于其开源的优良品质,涌现了一大堆手机厂商争奇斗艳,能占有一席之地的手机型号也比比皆是。这么多的设备,屏幕大小也层次不齐,好在谷歌早就考虑到了这一点

image.png

提倡在开发过程中使用“**密度无关像素density- independent pixel**”来进行布局开发,dip 也就是我们常见的dp。

这里先介绍几个概念:

px: 像素,画面的基本显示单位。屏幕的分辨率一般就描述为“宽向像素数X纵向像素数”,比如 Android手机常见的分辨率有:320 X 480, 1080 X 1920等,开发中设置的尺寸大小最终都要转换成px进行展示。不同的手机,1px的实际展示大小也不一样,所以开发中不建议使用px的方式进行硬编码

dpi: dots per inch (每英寸的像素数),即像素密度。像素密度越大,屏幕画质显示更加细腻。安卓手机种类多样,常见的有160dpi 是中密度(mdpi),240dpi是高密度(hdpi),320dpi是超高密度(xhdpi),480dpi是超超高密度(xxhdpi)。其中,Android 系统定义的屏幕像素密度基准值是 160 dpi,该基准值下 1 dp 就等于 1 px,同理 320dpi 对应 1dp=2px...

density:   显示的逻辑密度,是系统根据当前像素密度指定将dp单位转换为像素时所必须使用的缩放系数****。该设备的density为1时,1dp对应1px;density=2时,1dp=2px...

dp: 理解了上面的概念后,我们可以推理出,dp = (dpi/160)px。所以开发中使用dp作为尺寸单位,系统在渲染时会处理dp与px的关系,也就可以达到适配不同手机屏幕的效果了。

image.png

## 二. 主流方案

既然dp已经处理了适配,为什么实际效果还是不尽如人意呢?


 举个🌰

设计师给出的视觉稿分辨率为 1286x2778 ,density = 3,绘制一个500px的矩形框 --矩形框占比  500/1286=0.39

我们的设备:

  1.  华某为 1160x2700 ,densityDpi=520 ,density = 3.25  -->  1dp = 3.25px 矩形框占比  0.47
  2. 三某星  1440x3200,densityDpi=576,density=3.6 --> 1dp=3.6px  矩形框占比   0.42

image.png

不难看出并非所有的手机density都和视觉稿匹配,难免与视觉稿的比例有些出入,自然展示到手机上的效果无法令设计师满意了。


 痛定思痛,就没有一个通用的解决方案可以让我们在视觉还原上扬眉吐气一把吗?

1. 修改逻辑密度:

回顾上面所讲的内容,density 作为系统提供的缩放系数,是一个可以供外部修改的值。我们将视觉稿中px尺寸按照设计稿规范换算为dp,更新后的 density = 设备分辨率宽/视觉稿dp宽,这样便可以保证在不同的手机上显示效果和视觉稿大差不差了

image.png

接着上面的例子看

华某为 1160x2700 ,densityDpi=520 ,density = 1160/(1286/3) = 2.7  -->  1dp = 2.7px  矩形框占比  0.39

三某星  1440x3200,densityDpi=576,density=1440/(1286/3) =3.36  --> 1dp=3.36px 矩形框占比  0.39

image.gif

2. 宽高限定符:

这是一种比较原始的解决方案,核心手段就是穷举市面上各种的分辨率,然后以视觉稿的尺寸作为基准生成不同的 dimens 文件


 视觉稿 1080x1920

手机1(1080x1920):

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="x1">1px</dimen>
    <dimen name="x2">2px</dimen>
    <dimen name="x3">3px</dimen>
    ....
    <dimen name="x1080">1080px</dimen>
    <dimen name="y1">1px</dimen>
    <dimen name="y2">2px</dimen>
    <dimen name="y3">3px</dimen>
    ....
    <dimen name="y1920">1920px</dimen>
</resources>

 手机2(1440x2400):

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="x1">1.3px</dimen>
    <dimen name="x2">2.6px</dimen>
    <dimen name="x3">3.9x</dimen>
    ....
    <dimen name="x1080">1440px</dimen>
    <dimen name="y1">1.25px</dimen>
    <dimen name="y2">2.5px</dimen>
    <dimen name="y3">3.75px</dimen>
    ....
    <dimen name="y1920">2400px</dimen>
</resources>

 高度也可以适配上,问题也显而易见 需要精准匹配。

手机型号层出不穷,生命不息 适配不止😭


 三. 业务经历

在笔者所经历的项目中,采用的是修改逻辑密度的适配方案。使用这种方案时需要注意 用户可能会手动设置屏幕的效果参数(设置字体显示模式、设置屏幕分辨率等),所以处理density参数时还需要考虑当前系统设置的缩放比例

image.png

而且 这种方案有个明显的弊端就是 我们只处理了宽度的适配而忽略了高度。一般来说,确实可以不用处理高度适配,超出屏幕的显示内容一般可以使用ScrollView之类的组件进行包装处理。但是我们也经常处理一些类似半屏弹窗(BottomSheetDialogFragment)的组件,不排除设计师对这类组件的展示高度有近乎疯狂的要求,很痛苦 这种时候我们建议对高度进行百分比布局。