一、三个概念:px、dpi、dp
-
px:物理像素 / 分辨率里的那个像素 当我们说"720p、1080p"时,其实说的是分辨率:
- 720p ≈ 1280 × 720 像素
- 1080p ≈ 1920 × 1080 像素 这里的"1280、1920、720、1080",都是 px(物理像素点的个数)。
-
dpi:像素密度(每英寸多少个像素) dpi(dots per inch)表示:一英寸(2.54cm)长度上有多少个像素点。
- 160dpi:每英寸约 160 个像素
- 320dpi:每英寸约 320 个像素(比 160dpi 更细腻) dpi 是硬件属性,跟屏幕多大、分辨率多少一起决定"看起来多清晰"。
-
dp:密度无关像素(React Native 用的逻辑单位) dp(density-independent pixel)是系统定义的一种"逻辑长度单位",让同样的数值在不同 dpi 的设备上,看起来差不多大。 在 React Native 里,你写
width: 100,这个 100 本质上就是 100dp,而不是 100px。
二、dp 和 dpi 的数学关系
Android / React Native 的约定:
- 把 160dpi 的屏幕当成基准屏幕。
- 在 160dpi 屏幕上:
1dp ≈ 1px - 在其它密度屏幕上,系统用这个公式换算:
px = dp × (dpi / 160)
举几个数字例子:
- 160dpi(基准)
1dp = 1 × (160 / 160) = 1px100dp = 100px - 320dpi
1dp = 1 × (320 / 160) = 2px100dp = 200px - 480dpi
1dp = 1 × (480 / 160) = 3px100dp = 300px(四舍五入后近似)
也就是说:
当你在代码里写 width: 100 时,这个 100 实际上是 100dp。系统会根据当前设备的 dpi,把它换算成应该使用多少个物理像素:
- 在 160dpi 的屏幕上,大约使用 100px(1dp ≈ 1px);
- 在 320dpi 的屏幕上,大约使用 200px(1dp ≈ 2px);
- 在 480dpi 的屏幕上,大约使用 300px(1dp ≈ 3px)。
随着屏幕像素密度提高,单个像素本身变得更小,所以虽然不同设备实际使用的物理像素数量不一样,但这个"100dp 宽"的控件在现实世界里的物理尺寸(比如看起来有多宽、占多大一块屏幕)会尽量保持接近。
现在,160/320/480dpi 和 dp→px 的关系就理清楚了,也解释了"为什么同样的 dp 在不同手机上看起来差不多大"。
三、示意图:同样 100dp 的按钮,在不同 dpi / 分辨率上长什么样?
1)左边:720p,160dpi
- 分辨率:1280 × 720 px
- dpi:160
- 换算:
1dp = 1px,100dp = 100px - 按钮在屏幕上大约占据"某个具体宽度 A 厘米"。
2)中间:1080p,320dpi
- 分辨率:1920 × 1080 px
- dpi:320(像素更密)
- 换算:
1dp = 2px,100dp = 200px - 按钮用更多像素画出来,但因为像素更小,实际占据的物理宽度依然接近 A 厘米。
3)右边:更高分辨率,2K,480dpi
- 分辨率:例如 2560 × 1440 px(举例)
- dpi:480
- 换算:
1dp = 3px,100dp = 300px - 按钮在屏幕上依然大约是 A 厘米宽,只是边缘、文字更细腻。
图中三个按钮,看起来差不多宽,但右边那台的像素数量最多------这就是"dp + dpi 换算"的效果:同样的 dp 值,在高密度屏上用更多 px 去画,从而保持视觉尺寸接近。
四、那 720p、 1080p 在这个故事里扮演什么角色?
- 720p、1080p 说明的是总像素数(分辨率),即屏幕一共多少个 px。
- dpi 说明的是这些像素在物理尺寸上的"密度",和"屏幕有多大"一起决定屏幕看起来多清晰。
- dp 是在 React Native / Android / iOS 布局里用的"逻辑尺寸单位",用来写 UI 尺寸。系统会根据当前设备的 dpi 和分辨率,用公式把 dp 换成 px。
你可以大致这样理解:
- px:砖头总数(分辨率)
- dpi:每 1cm 摆多少块砖(密度)
- dp:设计图上画的"这堵墙要 2 米宽",具体到现场要用多少砖,由工人(系统)根据砖尺寸(dpi)来算。
五、在 React Native 里怎么用 dp 写尺寸?
在 RN 代码里,你不用写 dp、px 这样的单位,直接写数字即可,这个数字默认就是 dp:
javascript
// 宽高都是 dp 单位
<View style={{ width: 100, height: 40, borderRadius: 8 }} />
React Native 会做几件事:
- 获取当前设备的:
- 分辨率(总px数)
- 屏幕尺寸(英寸)
- 像素密度dpi(ppi)
- 用公式 px = dp × (dpi / 160),把你的dp换算成真实px
- 把这个px告诉底层原生视图去渲染
你也可以用 Dimensions 和 PixelRatio 看一下当前设备的逻辑尺寸和像素比:
js
import { Dimensions, PixelRatio } from 'react-native';
const { width, height } = Dimensions.get('window'); // 宽高,单位是 dp
const pixelRatio = PixelRatio.get(); // 设备像素比,约等于 dpi / 160
比如某台设备:
- width = 375,height = 812(单位是逻辑尺寸dp)
- pixelRatio = 3
- 分辨率大约是:宽 375 × 3 = 1125px,高 812 × 3 ≈ 2436px(类似iPhoneX)
六、和设计稿的连接:从px到dp
在开发中,你最困惑的可能是: "设计师给我一张 750px 宽的图,我为什么要写 375?"
为什么要"除以 2"?
这得从移动端设计的行业标准说起。
1. 什么是"2 倍图"?
在 Retina(视网膜)屏幕出现之前,屏幕很渣(1倍屏),1个逻辑像素 = 1个物理像素。
后来 iPhone 4 搞出了 Retina 屏,屏幕大小没变,但塞进去的像素翻了一倍(横竖各翻倍)。
这就出现了一个标准: "2 倍图标准" (以 iPhone 6/7/8 为基准):
- 手机物理屏幕(硬件): 宽 750px(很细腻)。
- 系统逻辑宽度(软件): 宽 375dp(也就是系统认为只有 375 个"点")。
- 关系: 750 ÷ 375 = 2 。这就是 "2 倍图" (@2x) 的由来。即:2 个物理像素 = 1 个逻辑点 (dp) 。
2. 设计师为什么喜欢给 750px 的稿子?
虽然系统逻辑只要 375dp,但为了保证图标、线条在高通过率屏幕上不模糊,设计师通常会在 750px × 1334px(也就是 iPhone 6/7/8 的物理分辨率)的画布上画图。
这就好比:设计师在一张很大、很清晰 的纸(750px)上画图,但你的手机屏幕是一个缩小镜,它要把这张大纸缩放到 375dp 的逻辑宽度里去显示。
3. 开发者怎么换算?(核心公式)
因为 RN 代码里写的是 dp(逻辑点),而设计稿是 px(物理像素,且通常是 2 倍密度的)。
所以,拿到一张 750px 宽 的设计稿,你的换算逻辑是:
RN 数值 = 设计稿数值 ÷ 2
实战例子:
-
场景:设计师用 Figma 给了一张 750px 宽的设计稿。
-
元素 A :设计稿上量出来宽 200px。
- 你的思考:这是 2 倍图,所以我除以 2。
- 代码写 :
width: 100
-
元素 B :设计稿上量出来字号 32px。
- 你的思考:除以 2。
- 代码写 :
fontSize: 16
4. 特殊情况:如果是 3 倍图呢?
现在有的设计师喜欢用 iPhone 12/13/14 Pro 的尺寸做图,这类手机的画布宽度通常是 1125px 或者更高。
- 基准逻辑宽度:依然约等于 375dp ~ 390dp。
- 换算关系:1125 ÷ 375 = 3。
- 结论 :如果设计师给你的是 3 倍图(超高清稿),你就 除以 3。
总结:
问设计师一句:"你这是按几倍图画的?"
- 如果是按 750px 宽画的,就是 2 倍图 -> 除以 2。
- 如果是按 375px 宽画的(1 倍图),就是 1 倍图 -> 不除,是多少写多少。