Android屏幕适配

57 阅读4分钟

1. 基础概念

  • 像素密度(density) dpi

像素密度是屏幕单位面积内的像素数,称为 dpi(每英寸的点数).

  • 每英尺的像素点数 ppi (pixels of per inch)

ppi = √(宽^2 + 高^2)/ 屏幕尺寸

dpi不等于ppi,dpi是android中软件定义的,系统可以自定义修改;ppi是屏幕的物理参数,无法修改。

  • 度无关像素 (density-independent pixels)

尺寸相同的两个屏幕可能具有不同数量的像素,直接使用相同的像素会导致2个屏幕上的内容看起来大小不一样;但是如果定义2个设备屏幕dp值大小相等,使用相同的dp值则可以让2个屏幕上的内容看起来大小一样。

px = dp * (dpi / 160)

dp 是一个虚拟像素单位,1dp 等于中密度屏幕(160dpi)上的 1px。

支持不同的像素密度

2. 基于资源限定符方案

阅读官方文档,了解下各种限定符

应用资源概览

备用布局资源

表 2. 配置限定符名称及优先级

假如将手机上的内容不做缩放显示到大屏设备上,视觉可能看起来太小了;

假如按比例缩放后显示到大屏设备上,视觉上就是单纯的放大,不符合大屏设备的UI/UX体验。

因此按比例缩放仅适用于尺寸相近的设备上,如果差异过大,那么缩放后的效果同样非常不好。

理论上设计师应该给出各种尺寸的设计方案/设计稿,但是现实情况往往设计师只做出一套设计,开发人员需要基于这一套设计适配到不同尺寸的屏幕上。

设计/开发一般都是基于宽度做方案。

比如存在以下两个分辨率的屏幕,dpi都是160

  • 设备A 2560*1600 sw1600dp w2560dp 宽高比16:10

  • 设备B 2560*1440 sw1440dp w2560dp 宽高比16:9

那么此时使用 w2560dp 即可适配绝大部分场景。

假如还有其他尺寸,

  • 设备C 3200*2000 dpi=240 sw1333dp w2133dp 宽高比16:10

  • 设备B 3840*2160 dpi=120 sw2880dp w5120dp 宽高比16:9

那么此时可以基于A、B设备尺寸等比缩放适配,因为我们使用dp单位,所以可直接根据dp值的比例进行缩放。

设备C 对比 设备A,w2133dp/w2560dp=0.883,缩放了0.883倍,那么可以将资源使用的dp值也缩放0.883倍。

例如:设备A(w2560dp)撑满屏幕宽度应该使用2560dp,设备C(w2133dp)撑满屏幕宽度应该使用2133dp。

插件自动缩放

基于以上思路,这里开发了一套dimens缩放插件 scale-dimens

使用方式:

// pluginManagement  声明使用jitpack仓库
pluginManagement {
   repositories {
      maven {
         url = uri("https://www.jitpack.io/")
      }
   }
}

// 声明使用插件
plugins {
    id("io.github.liu-wanshun.scale-dimens") version "2.0.0-SNAPSHOT"
}
// 在需要缩放的模块下声明配置configPath,比如app模块或者其他lib模块
scaleDimens {
    configPath = rootProject.file("scale-demens.yaml")
}

参数说明:

baseQualifier:基于哪个限定符缩放(config下面可以声明一组或多组baseQualifier)

generateQualifier:将要生成的限定符列表(在generateQualifier下面声明内容,可以多个)

qualifier:生成的限定符

scale:缩放比例

以下举例配置

config:
  - baseQualifier: "sw1600dp"
    generateQualifier:
      - qualifier: "w25600dp"
        scale: 1f

      - qualifier: "w2133dp"
        scale: 0.833f

  - baseQualifier: "w25600dp"
    generateQualifier:
      - qualifier: "w25600dp"
        scale: 1f

      - qualifier: "w2133dp"
        scale: 0.833f

配置之后,每次编译时都会根据scale-demens.yaml的配置进行生成缩放文件,缩放生成的文件可以在这个路径下查看

模块/build/generated/res/scaleDimensXXXXX

缩放文件会打包到apk中,无需人工处理。

这样开发人员最少仅需维护一套base的dimens,其他交给插件自动缩放处理即可,对项目代码完全无侵入。

此外需要注意几点:

  • 如果app参与分屏,或者旋转屏幕,那么可用宽度(w)可能会变更的,可以考虑其他限定符作为补充

3. 基于修改应用config方案

在你使用的Context(例如activity)在attachBaseContext时注入自定义config; 也可以直接使用自定义的context来加载资源。

override fun attachBaseContext(newBase: Context) {
    val newConfig = Configuration().apply {
        // 这里设置你想要的config,比如固定屏幕参数/uimode/字体大小/地区语言等等都可以
        // screenWidthDp
        // smallestScreenWidthDp
        // uiMode
        // densityDpi
        // fontScale
    }
    val newContext = newBase.createConfigurationContext(newConfig)
    super.attachBaseContext(newContext)
}

这样使用这个context关联的resource加载资源时都会使用自定义的config了。

每个需要使用的context都需要特殊处理,存在一定的侵入性。