本节摘要:屏幕适配的核心是三组单位——px(物理像素)、dp(密度无关像素)、sp(可缩放像素)。换算公式:px = dp × dpi ÷ 160。布局尺寸一律用 dp、字号一律用 sp,界面才能在不同密度的屏幕上呈现一致的物理大小。本节从像素密度讲起,把换算、限定符适配、常见坑一次讲透。
先把概念摆正。分辨率是屏幕的像素总数,比如 1080×2400;尺寸是对角线物理长度(英寸);像素密度 dpi(dots per inch)= 对角线像素数 ÷ 对角线英寸数。同样 1080px 宽,6.7 英寸大屏的 dpi 低、5.0 英寸小屏的 dpi 高——像素数相同,物理大小却不同。这就是"在 XML 里写 px"注定失控的原因:100px 在高密度屏上只是一小条,在低密度屏上却宽得出格。
Android 给屏幕密度分了六档,写适配前先背下这张表:
| 密度档 | dpi 范围 | 1dp 等于多少 px |
|---|---|---|
| ldpi | ~120 | 0.75px |
| mdpi | ~160 | 1px(基准) |
| hdpi | ~240 | 1.5px |
| xhdpi | ~320 | 2px |
| xxhdpi | ~480 | 3px |
| xxxhdpi | ~640 | 4px |
系统以 mdpi(160dpi)为基准:px = dp × dpi ÷ 160。验证一下:48dp 的触摸目标在 xxhdpi(480dpi)屏上 = 48 × 3 = 144px;在 hdpi(240dpi)屏上 = 72px。像素数差了一倍,但两台机器上按出来的物理面积几乎一样——这就是"密度无关"的含义。
dp 解决布局尺寸,sp 解决文字。二者换算规则相同,唯一区别:sp 会跟随用户在系统设置里调整的字体大小缩放。用户把系统字号调到最大,用 sp 的文字跟着变大,用 dp 的纹丝不动。因此铁律是——凡是字号,一律 sp;凡是间距、控件尺寸、边框,一律 dp;px 基本只在代码里算位图时出现。
<!-- 单位使用的标准姿势 --> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="猜数字" android:textSize="28sp" <!-- 字号用 sp --> android:layout_marginTop="16dp" <!-- 间距用 dp --> android:paddingStart="12dp" /> <Button android:layout_width="wrap_content" android:layout_height="48dp" <!-- 触摸目标高度至少 48dp --> android:minWidth="48dp" android:text="猜!" />

dp 只保证"大小一致",不保证"布局合理"。同一界面在 320dp 宽的手机和 720dp 宽的平板上,控件会拉得忽宽忽窄。Android 的对策是资源限定符:为不同最小宽度提供不同目录的布局文件,系统按设备自动选用。目录名形如"最小宽度 600 以上"的修饰后缀,平板进入该项目时读平板专属布局。
另一个轻量手段是 weight 比例分配(第 4 章详讲),让两个按钮永远按 1:1 平分宽度,与屏幕宽窄无关。实战里通常组合使用:手机用一份默认布局,平板用限定符目录里的双栏布局。
⚠️ 常见坑三连:
- 字号写成 dp:用户调大系统字体后你的 App 无动于衷,视障用户体验直线下降;
- 用 px 硬写:换台手机控件大小错乱,代码评审一眼打回;
- 在代码里 setTextColor 之类传 px 参数:有些 API 收的就是 px,需要按密度手动换算,直接把 dp 数值丢进去会小得看不见。
实际项目里适配问题五花八门,但排查有固定套路可循。第一步永远问:这个尺寸写的是什么单位?ImageView 高度写死 dp 在大屏上偏小、文字用 dp 导致老年用户放大字体的需求失效,八成适配 bug 的病根都在单位错配上。第二步问:这个控件的尺寸来自哪里?match_parent、wrap_content、固定值、权重、约束各自有不同的失效场景,比如 wrap_content 遇到长文案就会在窄屏上溢出。第三步才轮到工具:模拟器开三个分辨率档各跑一遍,横竖屏各看一次,深色模式顺带扫一眼。三步走完还定位不了的,用布局检查器看真实测量值,第 8 章的标准流程在此候命。顺带建立一个数量级感觉:主流手机的密度桶大致在 hdpi 到 xxxhdpi 之间,一倍 dp 对应的物理像素从 1.5 到 4 不等——理解了这个倍率区间,"为什么同一个 dp 在不同手机上看起来差不多大"就不再是魔法。
再补一个新手几乎必踩的换算细节: dimens 里存的永远是 dp 或 sp 数值,换算发生在渲染时,也就是说你无法在代码里直接"读出"某个 dp 对应多少像素——需要换算时用 resources.displayMetrics 里的 density 因子乘出来,触摸手势的像素阈值(第 5 章的滑动判定)就是这种场景。理解"单位属于声明、换算属于系统"这条分工,单位混乱的问题就断了根。至于平板与折叠屏这类大屏适配,手段仍是同一套:约束、权重、限定词三件套换参数不换思想,等你的 App 真要上平板时,本章的底子直接够用。
一个日常协作细节作为本节的句号:和设计师对稿时养成报 dp 而不报 px 的习惯——设计稿标注 24px 在一倍图与三倍图上含义不同,统一换算成 dp 再沟通,可以消灭大半"尺寸对不上"的无效来回,也让单位纪律从个人习惯升级为团队协议。