0%

Android屏幕适配方案设计

相关概念

  • 屏幕分辨率

    经常说的1080*1920,表示宽高的像素个数。

  • 屏幕尺寸

    手机对角线的尺寸,单位是英寸,1英寸=2.54cm。

  • dpi

    每英寸的像素个数,比如10801920的手机,5.5英寸,那么dpi为sqrt(10801080+1920*1920)/5.5=354

    而这里,Android定义了一系列的密度类型,比如mdpi为160,hdpi为240,xhdpi为320,xxhdpi为480。

  • dp

    密度无关像素,1dp的像素数跟dpi有关,1dp=dpi/160。

  • sp

    独立比例像素,跟dp类似,但它还跟字体大小相关联。

资源文件夹命名

  • sw<N>dp

    最小宽度限制,比如layout-sw600dp表示当屏幕最小宽度至少为600dp时才会使用该资源。这里的最小宽度,是指屏幕宽高较小边,也就是固定不变的。

  • w<N>dp

    最小宽度限制,跟sw类似,区别就是这里的宽度就是屏幕宽度,屏幕方向改变,宽度也会随之改变。

  • h<N>dp

    最小高度限制,比较少用。

  • 具体分辨率

    如values-1920*1080,采用向下匹配的方式查找。

资源文件查找

drawable查找

一台hdpi的手机,drawable的查找顺序如下:

hdpi –> xhdpi –> xxhdpi –> xxxhdpi –> nodpi –> mdpi –> ldpi –> ldpi –> drawable

也就是优先从高但最接近的dpi中查找,nodpi结束;没有再从低但最接近的dpi查找,drawable结束。

另外,如果一个手机的dpi为354,那么它落在320~480的区间,对应为xhdp。

drawable内存占用

假如一张图片100*100,只放在xhdpi中,手机为xxhdpi,那么加载到内存中该图片会被放大,宽高放大的倍数为:手机dpi/xhdpi,放大可能图片也会变模糊。建议图片按主流手机的dpi切图,然后放到较高dpi的目录下,比如xxdpi目录。

values具体分辨率查找

经常会有values,values-1920x1080,values-1280x720这些文件夹,查找的规则就是往下查找,values放最后面,如果没有就停止,不会往上找,这个与drawable不一样。

适配方案

1. 适配技巧

  • 不硬编码px,尽量使用dp,sp,wrap_content,match_parent。

  • 使用.9图,矢量图,代码自定义View,动态计算。

  • dimens适配多个sw文件夹

    ├── res
    ├── ├──values
    ├── ├──values-sw320dp
    ├── ├──values-sw360dp
    ├── ├──values-sw400dp
    ├── ├──values-sw420dp
    ├── ├──values-sw460dp
    ├── ├──...
    ├── ├──values-sw560dp
    ├── ├──values-sw600dp
    ├── ├──values-sw640dp

    或者是特定分辨率文件夹

    ├── res
    ├── ├──values
    ├── ├──values-480x320
    ├── ├──values-800x480
    ├── ├──values-960x540
    ├── ├──values-1024x600
    ├── ├──values-1280x720
    ├── ├──...
    ├── ├──values-1920x1080
    ├── ├──values-2560x1440

    对于这两种方式的每一个values文件,定义一系列的值。具体可以参考:https://github.com/ladingwu/dimens_sw

    这种适配方式的缺点就是维护起来麻烦,多文件增大apk大小。

今日头条适配方案

核心思想

动态修改DisplayMetrics的density值,修改的公式为:

density = 手机屏幕宽度(px) / 设计图宽度(dp)

这样用dp指定宽度就会自动按比例分配。

原理

Android中View的大小最终都会转为px,而转换的逻辑就在TypeValue的applyDimension方法中:

public static float applyDimension(int unit, float value,
                                   DisplayMetrics metrics) {
    switch (unit) {
    case COMPLEX_UNIT_PX:
        return value;
    case COMPLEX_UNIT_DIP:
        return value * metrics.density;
    case COMPLEX_UNIT_SP:
        return value * metrics.scaledDensity;
    case COMPLEX_UNIT_PT:
        return value * metrics.xdpi * (1.0f/72);
    case COMPLEX_UNIT_IN:
        return value * metrics.xdpi;
    case COMPLEX_UNIT_MM:
        return value * metrics.xdpi * (1.0f/25.4f);
    }
    return 0;
}

可以看到这里value * metrics.density,所以我们根据设计图修改density,这里计算的结果就能按照设计图的比例进行分配。

实现

因为DisplayMetric对象是在Resource对象中,因此对于每一个Context中的Resource对象,我们都需要修改其density值,另外,densityDpi和scaledDensity也需要修改,这几个值的含义如下:

  • density

    1dp占用的像素个数,也就是dpi/160的值

  • densityDpi

    屏幕的dpi,也就是1英寸占用的像素个数

  • scaledDensity

    1sp占用的像素个数,正常情况下跟1dp一样,但会根据系统字体调整

优缺点

  • 优点

    简单,适用范围广,没有用到反射

  • 缺点

    可能会对旧布局产生影响,因为density被改了。