一文读懂 Android 屏幕适配:从基础到实践

3,133 阅读18分钟

1. 基础概念

1.1. px(物理像素,pixel)

屏幕上一个最小的点,用来显示颜色和图像。

  • 举例:一个屏幕宽度是 1080px,意思是水平方向上有 1080 个小点。

  • 一个单位的实际大小取决于屏幕密度(ppi/dpi):

1.2. dpi(dots per inch,每英寸点数)

屏幕密度,意思是每英寸有多少个墨点。

  • 主要用于打印机和扫描仪: DPI 最初用来描述打印机每英寸能打印多少个墨点,以及扫描仪每英寸能捕捉多少个点。
  • 次要用于屏幕(有时与PPI互换) : 虽然严格来说屏幕是用像素而不是墨点,但在某些语境下(尤其是在早期的Windows系统中),DPI 也被用来指代屏幕的像素密度,与 PPI 类似。
  • 关系: 在打印领域,DPI 越高,打印出来的图像就越精细。在数字图像处理中,DPI/PPI 决定了图像在显示或打印时的物理尺寸和清晰度。
  • android常见的密度级别 (Density Buckets)
    • 160 dpi:标准密度(mdpi)
    • 320 dpi:高密度(xhdpi)
    • 480 dpi:超高密度(xxhdpi)
    • dpi 越大,同样的图形看起来就越小越精细。

1.2.1. 开发时如何查看设备DPI

使用如下adb命令:

adb shell wm density

1.3. ppi(pixels per inch,每英寸像素数)

屏幕像素密度,和 dpi 类似,都表示“每英寸像素数”,但 PPI 更常用于描述物理屏幕。"。

  • iPhone 或安卓手机介绍里常见,比如 “iPhone 13 是 460 ppi”。

📌 区别:

  • DPI 通常用于衡量打印清晰度
  • PPI 通常用于衡量屏幕清晰度
  • 两者计算方式相同但应用领域不同

虽然从技术严谨性上讲,屏幕应该用 PPI 来描述像素密度,但 Android 平台选择使用 DPI 作为其屏幕密度分类的命名,更多是出于历史习惯、方便开发者理解和适配、以及内部系统将设备归类到标准密度的实用性考量。在实际开发中,当你看到 Android 相关的“DPI”时,把它理解为“像素密度”或“屏幕的 PPI”是完全正确的。

1.4. 分辨率(resolution)

分辨率指的是屏幕水平方向和垂直方向的像素数量

常见格式是:宽 × 高(px)

📌 举例:

  • 1920×1080(1080p):表示屏幕宽有 1920 个像素,高有 1080 个像素。
  • 1280×800:宽是 1280 像素,高是 800 像素。
  • 2400×1080:是很多安卓手机的常见分辨率。

1.4.1. 分辨率与清晰度的基础关系

理论上来讲像素点越多,图像细节越丰富、边缘越平滑。

在相同屏幕尺寸下,更高的分辨率意味着更多像素点填充画面,能呈现更细腻的纹理和更少的锯齿感。

1.4.2. ****分辨率常见误区:

  1. 分辨率是不是越大显示越细腻?

这个说法并不完全准确,还要加一个限定词,同样尺寸的设备,分辨越大,显示越细腻,其实就是指的ppi。

  1. 分辨率是不是越大越好?

不是,如果屏幕很小,但是分辨率特别高,会导致文字和图标过小,看不清楚,一般来讲,厂家会根据屏幕尺寸给出测算出观看距离,设定合适的分辨率。

视网膜屏(Retina):乔布斯在2010年iPhone 4发布会上提出:当屏幕像素密度≥300 PPI(每英寸像素数),且观看距离约25-30厘米时,肉眼无法分辨像素点

1.4.3. 开发时如何查看设备分辨率

使用如下adb命令:

adb shell wm size

1.4.4. 常说的2k屏、4k屏是什么

“K” 是 kilo 的缩写,代表“约一千像素”。

它指的是横向(宽度)像素的数量,但只是个近似命名,不是精准的技术指标。

常见 K 分辨率标准对照表
名称分辨率(宽×高)实际宽度 px应用场景
1K(HD)1280×720≈ 1,000 px手机、入门级视频
2K(Full HD)1920×1080≈ 2,000 px电视、主流显示器
2K(影院标准)2048×10802048 px数字电影放映
4K(UHD)3840×2160≈ 4,000 px高清电视、4K显示器
4K(影院标准)4096×21604096 px数字电影制作
8K(UHD)7680×4320≈ 8,000 px超高清电视、专业拍摄

💡 注意几点细节:

  1. 4K 不是 1080p 的四倍(而是指宽度约为 1K 像素的四倍);
  2. “K” 是一个命名惯例,不是严格标准,常常是市场化说法。

1.5. dp(密度无关像素,density-independent pixel)

Android 专用单位,是一种逻辑像素,用于适配不同屏幕密度。

  • 它让你的布局在不同设备上看起来差不多大,这里的大小指的是视觉上的大小,即物理上的。
  • 1dp 大约等于在 160 dpi 屏幕上 1px,就是说一个160dpi的屏幕,1dp=1px,如果是320dpi的屏幕,1dp=2px。公式: px = dp × (当前DPI/160)

为什么是160:160Android的基准密度(mdpi),源于早期设备的典型参数,以160为基准,可避免当时常用的dpi在计算时产生浮点运算(如120dpi比例=0.75,240dpi比例=1.5)。

这里大家直接记住就好了,就好像1cm=10mm一样,为什么是10,就是方便计算。

📌 举例:你写了一个按钮宽度是 100dp

  • 在低分辨率屏幕上,它可能是 100px 宽
  • 在高分辨率屏幕上,可能是 200px 宽(但看起来还是差不多大)

1.5.1. 其他设备的逻辑像素

对应android用的dp,苹果上的单位是pt,web端使用px,这些都是逻辑像素单位,有不一样的换算关系。尤其注意web端里面开发用到的px和上面介绍的px是不一样的,虽然缩写一样,但web端开发用的是CSS像素,上面介绍的是物理像素。

换算关系

  • 苹果:在@1x设备下,1pt=1px。
  • web:在 DPR(devicePixelRatio)=1 的设备上,1px(css像素)=1px(物理像素)。

1.6. 一句话总结

  • PX:屏幕上的「点」,物理像素,其物理大小会随设备密度变化。
  • DPI/PPI:描述「点有多密」
  • 分辨率: 屏幕上所有像素点的总数
  • DP:Android的「虚拟厘米」,是一种逻辑像素,保证显示一致

2. android如何适配不同屏幕

2.1. 设计稿与开发的关系

这里说一下常见的UI设计软件 Figma,它在开发模式下取的值是逻辑像素,也就是dp。这里dp和px的转换关系都基于160dpi的情况,即1dp=1px。Figma目前也不支持配置dpi。其它设备,如ios和web也同理,按一倍关系进行转换。

2.2. 如何解决不同屏幕适配问题

在基础概念上提到的dp,使得元素在不同设备的物理大小看起来是基本一致的。

但是这并不能彻底解决问题。例如下图,如果设计稿中的元素宽度进行设置,不同屏幕尺寸的显示虽然元素大小一致,但是由于屏幕的物理大小不一致,所以显示还是会有异常。

上图的示意代码编写方式是按设计稿的固定尺寸设置的,如果按照等分的方式设置(如下图),那么这个问题就不存在了,因为这里是系统在布局的时候自动计算出来的宽度。

那么有没有一种方式,在我编写代码的时候写固定尺寸,在其他不同设备显示时,也能实现类似等分的效果。

答案是肯定的,只需将这个元素按照屏幕的实际尺寸,和基准设备的尺寸做对比后,进行缩放就好了。

那么这个计算的过程是放在哪里做的呢,当然不是每次都手动计算,有自动化的工具可以自动生成。说这个工具之前先补充一下知识点:android尺寸资源文件资源限定符

2.3. android尺寸资源文件

在 Android 开发中,dimens 是一个非常重要的资源类型,它用于定义尺寸值。这些尺寸值包括长度、高度、宽度、边距、填充、文本大小等,它们通常以 DP(密度无关像素)SP(可伸缩像素) 为单位。

默认的尺寸资源文件在res/values/dimens.xml

示例dimens.xml文件

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="spacing_small">8dp</dimen>
    <dimen name="spacing_medium">16dp</dimen>
    <dimen name="spacing_large">24dp</dimen>

    <dimen name="button_height">48dp</dimen>
    <dimen name="button_corner_radius">4dp</dimen>

    <dimen name="text_size_body">16sp</dimen>
    <dimen name="text_size_headline">24sp</dimen>

    <dimen name="avatar_size">56dp</dimen>
    <dimen name="card_elevation">2dp</dimen>
</resources>

如何在布局文件中使用:

<!-- 没使用dimens -->
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="16sp"
    android:padding="16dp"
    android:layout_marginTop="24dp"
    android:text="Hello World" />

<!-- 使用dimens -->
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="@dimen/text_size_body"
    android:padding="@dimen/spacing_medium"
    android:layout_marginTop="@dimen/spacing_large"
    android:text="Hello World" />

这实际上就是把硬编码的地方改为引用变量的方式,为我们做屏幕适配打下良好的基础。

2.4. 资源限定符

dimens.xml结合 资源限定符 (Resource Qualifiers) ,你可以为不同屏幕尺寸、不同像素密度、不同横竖屏模式等设备提供不同的尺寸值。

示例:

res/values/dimens.xml

<dimen name="detail_image_height">200dp</dimen>

res/values-sw600dp/dimens.xml (针对 7 英寸及以上平板):

<dimen name="detail_image_height">300dp</dimen>

手机在运行时会识别出当前设备的最小宽度(或其它限定符),然后将对应宽度的资源应用在UI上。

同一个 detail_image_height 在手机上是 200dp,而在平板上就是 300dp,从而更好地利用屏幕空间,提供更优的视觉效果。

这里values-sw600dp的意思是:如果设备的最小宽度(Smallest Screen Width,sw大于或等于600dp,系统将使用values-sw600dp文件夹下的dimens.xml否则,将使用默认的values文件夹下的dimens.xml

这里的资源限定符是最小宽度,还有别的资源限定符,如高度、宽度等等。

2.4.1. 如何创建values-swdp文件

目录视图模式切换为Android

选择dimens文件夹→鼠标右键→选择New→选择Values Resource File

找到最小宽度资源限定符→点击>>符号:

填写固定的文件名称dimens.xml→填写指定的最小宽度,这里假设为600→点击OK按钮:

将视图切换到Project,会发现在res文件夹下面自动创建了一个名称为values-sw600dp的文件夹,并且里面有一个新的dimens.xml文件:

2.5. 自动化适配插件ScreenMatch

自己一个一个文件夹创建太麻烦了,而且也不知道对应关系,更不知道里面具体变量的值要填什么。这里推荐一个插件叫ScreenMatch

2.5.1. 安装

打开Settings→在Marketplace中搜索ScreenMatch→点击INSTALL按钮:

2.5.2. 使用

使用方式很简单:在项目目录中,在任意文件或文件夹上右键点击鼠标→选择ScreenMatch→点击ScreenMatch:

选择对应的模块→点击OK按钮:

然后就会自动生成对应的dimens、一个配置文件screenMatch.properties和一个示例dimens文件:

2.5.3. 配置说明

screenMatch.properties文件:

base_dp=360
match_dp=1300
ignore_dp=240
  • base_dp:就是把UI分成多少份,360就是分成360份,为啥取360,因为主流的手机尺寸都是360的倍数,能够整除,值为360可以覆盖90%的手机机型。如果要修改,建议改为和你设计稿的最小宽度一样的值,这样默认的变量和值就是1:1,方便开发。
  • match_dp:在插件运行时,默认会生成多个dimens文件。如果默认生成的dimens文件中不包含你希望适配的屏幕尺寸,你可以在此处添加。例如,上述配置会额外生成1300dp宽度的dimens文件。
  • ignore_dp:不生成对应的尺寸的dimens,例子中是取消生成240dp下的dimens

screenMatch_example_dimens.xml文件:

  • 这是一个默认dimens的示例文件,仅供参考,不会在实际应用中被直接使用。如果需要,你可以将其内容复制到默认的dimens.xml文件中。

2.5.4. 常见问题

2.5.4.1. screenMatch适用范围

一般来讲,我们说的多尺寸适配,就是出一份设计稿,开发一次,在不同尺寸的设备都有良好的视觉体验,然而,这种方法主要适用于尺寸和宽高比相近的设备。ScreenMatch的原理是根据设备的DPI和分辨率,对不同宽度的设备进行等比缩放。如果设备尺寸与UI设计稿的差异过大,则难以提供良好的视觉体验,因此这种适配方式并非万能。

比如一样的文本和图片,在很小的设备上,强行压缩会看不清。同样,给手机设计的UI稿,放大后放在平板上,元素也会变得很大,浪费空间。

真的差异大的设备,还是需要出多份设计稿,至少手机、平板、横竖屏都得分别设计。

2.5.4.2. 使用screenMatch,如何计算当前设备匹配了哪个dimens.xml

公式: sw/(dpi/160),结果是多少,就匹配的是哪个。

例子: 如果设备的分辨率是1280x800px,dpi是320,结果为800/(320/160)=400,也就是说会匹配values-sw400dp下的dimens.xml

2.5.4.3. 为什么使用sw而不用w作为资源限定符

sw指的是最小宽度,而w指的就是宽度。假如,设备分辨率是1080 x 1920px,dpi是320,那么sw就是1080/(320/160)=540,不管是横屏还是竖屏,都是540dp;

而如果取w作为资源限定符,竖屏的时候和sw一样是540dp,横屏的时候就变成1920/(320/160)=960了,也就是说取w的时候切换横竖屏,缩放比例是不一样的。

还有一点,用w是很难确定当前尺寸是一个什么类型的设备,例如手机的横屏可能比中型平板的竖屏宽度要大,那这个设备是手机还是平板就很难区分,用哪套设计稿也很难判断,sw就不会,宽度就固定是短的那个,可以用来区分手机、中型尺寸平板,大型尺寸平板等。

如果有特殊需求,可以通过设置screenMatch.properties文件里面的create_values_sw_folder属性做到。

2.5.4.4. 为什么基于宽度做适配,而不是高度

因为一般的交互都是竖向滚动,横向的很少。只要横向适配好了,高度溢出的部分用ScrollView处理就好了。

2.5.4.5. 使用默认的base_dp配置显示异常

如果说你想要适配平板等更大尺寸的设备,需要修改一下base_dp,原来默认的360是手机的尺寸。

常见尺寸:

  • 360: 主流尺寸手机
  • 600:7寸平板
  • 720、820:10寸平板

2.5.5. 使用ScreenMatch的开发设计流程

  • 设计师依据目标设备定基准尺寸:
    • 如果是手机的设计稿:使用360宽度的设备作为基准设备进行UI设计
    • 如果UI平板:依据主流平板的宽度作为基准进行UI设计
    • 如果是特定设备:直接取特定设备的宽度(px)为基准进行UI设计
  • 开发人员配置ScreenMatch:
    1. 先运行ScreenMatch生成默认配置
    2. 依据需要,修改base_dp填入设计师UI稿的宽度(注意,这里是最小宽度,即横竖屏中较短的那条边)。
    3. 重新运行ScreenMatch,更新配置
  • 开发人员进行开发验证:
    • 找一个简单的页面,按设计师出的UI做出对应的布局文件,里面的尺寸全部用dimens变量进行设置。
  • 开发人员选择对应尺寸的设备或新建对应尺寸的设备,验证这个页面的显示效果是否与UI一致:
    • 如何切换预览设备尺寸:

    • 如何新建尺寸:

拉到最下面,点击Add Device Definition

点击New hardware profile

分辨率改成和你要测试目标设备的分辨率一致,点击FINISH就可以使用了:

2.5.6. 总结

ScreenMatch在你写固定尺寸值的时候,在大小差不多的设备做适配,原理是通过资源限定符和字符串资源文件,实现动态引用,其缩放值由当前设备像素,像素密度与基准设备(UI稿)的比例共同决定。

2.6. 自动化适配库AndroidAutoSize

这是一个今日头条屏幕适配方案,与插件不一样,是通过引入第三方库的方式实现的屏幕适配,库的地址放在参考连接里了。

2.6.1. 如何使用

官方有详细的使用说明,这里简单说一下:

2.6.1.1. 安装

在项目根目录的build.gradle加入jitpack仓库地址:

allprojects {
    repositories {
        ...
            maven { url "https://jitpack.io" }
    }
}

在app级build.gradle加入依赖

dependencies {
    implementation 'com.github.JessYanCoding:AndroidAutoSize:v1.2.1'
}
2.6.1.2. 配置

AndroidManifest中填写设计稿的尺寸:

<manifest>
    <application>            
        <meta-data
            android:name="design_width_in_dp"
            android:value="360"/>
        <meta-data
            android:name="design_height_in_dp"
            android:value="640"/>           
     </application>           
</manifest>

注意:这里官方没提,经验证,使用的是设计稿的宽度,不是最小宽度,要注意选横向的那个长度,填错了适配会有问题。

2.6.1.3. 基本原理

动态修改 Android 系统的 DisplayMetrics 密度值,让系统按照我们期望的比例计算 dpsp 等单位的实际像素大小。

DisplayMetrics 的常用字段:

属性含义
widthPixels屏幕宽度(像素)
heightPixels屏幕高度(像素)
density逻辑密度值,相当于 dp 与 px 的比例(如 1.0 / 2.0)
densityDpi每英寸像素数(DPI),如 mdpi=160,hdpi=240 等
scaledDensity字体缩放比例,根据用户字号设置自动调整
xdpi & ydpi屏幕 X/Y 方向的真实物理像素密度

适配原理与ScreenMatch的区别:

  • ScreenMatch:按特定dpi生成缩放后的变量供开发时候引用
  • AndroidAutoSize:在运行时修改缩放比例进行页面适配

2.7. ScreenMatchAndroidAutoSize的优缺点

ScreenMatch

  • 优点

    • 直接预览布局:可以在 Android Studio 中方便创建虚拟预览设备(以 dp、pt、in、mm 为单位),快速预览各种屏幕尺寸下的布局效果 。
    • 运行时无额外开销:仅在设计阶段生成适配资源,运行时不做动态计算,对性能无影响 。
  • 缺点

    • 依赖资源文件定义:如果项目之前大量使用硬编码 dp 值,则必须统一替换为 dimens 并维护多套资源文件,工作量较大。
    • 适配新尺寸需重新生成资源并打包:每次支持新设备,都需要重新生成相应 dimens 和资源包。

AndroidAutoSize

  • 优点

    • 侵入性小:无需改动现有 dp/sp 布局,几乎不用修改原始硬编码布局 。
    • 支持运行时动态调整:通过修改 DisplayMetrics(density、scaledDensity、densityDpi),实现运行时根据不同屏幕尺寸按比例缩放,适配新设备无需重新打包 。
    • 安装配置简单:通过 AndroidManifest 中配置 design_width_in_dp 即可使用,支持按宽或按高适配,灵活方便 。
  • 缺点

    • 兼容风险:AndroidAutoSize 会修改 Application 和 Activity 的 DisplayMetrics,这可能影响依赖原始 density 的第三方库或系统组件(如 WebView、地图 SDK),如果遇到厂商系统对 density 做了特别处理,有可能出现显示异常 。
    • 开发预览不方便:实时 preview 因为依赖系统默认 density,而库运行时修改了 DisplayMetrics,因此在 IDE 预览时可能无法正确反映最终效果,需要开发者自行新建虚拟设备(虚拟宽度/高度)以 dp 为单位去预览。

2.8. 总结

自动适配可以通过ScreenMatchAndroidAutoSize实现,但本质都是通过一定的计算方式,前置或后置的对元素进行缩放,以在相近尺寸、不同dpi屏幕上有一样的视觉体验。

3. 参考