一、问题背景
最近在做项目适配时,我遇到了一个非常诡异的问题:
我的应用需要适配不同尺寸的手表屏幕,因此我在 res/
目录下为不同设备准备了多份 dimens.xml
文件,例如:
res/├── values/├── values-w320dp-h374dp/├── values-w360dp-h390dp/├── values-w388dp-h450dp/
大多数设备都能正确匹配各自的 dimens
文件,唯独一台 120dpi、分辨率为 240×280 px 的设备始终无法命中对应的 values-w320dp-h374dp
。
安装后应用总是加载默认的 values/
文件,看似 Android 完全忽略了那一份适配资源。
二、我一开始的误区
起初我以为问题出在 DPI 与分辨率不匹配,
但因为我在文件夹名里已经用的是 dp,而不是 px,理论上系统应该能自动计算出相应的逻辑尺寸。
可事实证明:Android 的资源匹配逻辑并不是简单地用公式换算出来的 dp 值。
三、真正的原因:screenWidthDp
与 screenHeightDp
Android 在匹配诸如 values-wXXXdp-hXXXdp
的目录时,并不是用分辨率直接换算,而是用系统内部维护的两个配置字段:
Configuration.screenWidthDp
Configuration.screenHeightDp
这两个值代表当前屏幕在 逻辑 dp 下的“可用区域”,
是系统根据 densityDpi、方向、系统栏、可用窗口等综合计算出来的整数值。
——注意,是 整数值!
四、计算过程举例(关键)
我们那台 120dpi 的设备分辨率为 240×280 px。
Android 的换算公式为:
dp = px × 160 / densityDpi
计算得:
宽度:240 × (160 / 120) = 320.0 dp
高度:280 × (160 / 120) = 373.3 dp
✅ 宽度是 320dp,刚好对上;
❌ 高度是 373.3dp,不到 374dp。
而 Configuration.screenHeightDp
在系统中是取整后的整数(通常向下取整),
所以系统认为该设备的可用高度是 373dp,而不是 374dp。
因此:
values-w320dp-h374dp ❌ 不匹配
values-w320dp-h373dp ✅ 完美匹配
五、验证方法
可以在任意 Activity 中打印出系统真实使用的逻辑尺寸:
Configuration c = getResources().getConfiguration();
Log.i("ScreenDP", "screenWidthDp=" + c.screenWidthDp + ", screenHeightDp=" + c.screenHeightDp);
输出结果类似:
screenWidthDp=320, screenHeightDp=373
这两个值才是 Android 资源选择系统真正参考的指标。
六、如何正确适配
✅ 方法一:根据实际 screenWidthDp / screenHeightDp
创建目录
在每个目标设备上打印这两个值,然后根据结果命名目录:
values-w320dp-h373dp/
values-w360dp-h390dp/
values-w388dp-h450dp/
✅ 方法二:使用 swNNNdp
(最小宽度)适配
swNNNdp
(smallest width)基于设备最小边的 dp 尺寸,不受方向和系统栏影响,更稳妥:
values-sw320dp/
values-sw360dp/
values-sw388dp/
✅ 方法三:只按宽度区分
如果不同设备只是比例略有差异,可以仅根据宽度划分目录,简化适配。
七、延伸思考:为什么要“向下取整”
Android 在比较匹配条件时,为了避免资源抖动(例如状态栏隐藏/显示导致高度轻微变化),会将 dp 尺寸以整数形式存储。
也就是说即使设备计算出的高度是 373.9dp,系统仍然认为是 373dp。
这也是为什么即便你写了 374dp,看起来只差 0.1dp,却完全不会命中的原因。
八、总结
设备参数 | 结果 |
---|---|
分辨率 | 240×280 px |
densityDpi | 120 |
实际逻辑大小 | 320×373dp |
系统用于匹配的值 | w320dp-h373dp |
正确目录 | values-w320dp-h373dp ✅ |
✏️ 写在最后
这次经历让我重新理解了 Android 的资源匹配逻辑:
“Android 匹配的不是你以为的分辨率,而是系统眼中的逻辑 dp 尺寸。”
如果你也在做屏幕适配,不妨动手打印一下 screenWidthDp
/ screenHeightDp
——
你会发现很多“为什么匹配不到”的谜团,其实都藏在这两个数字里。