ArkUI 尺寸单位(对照 Android):vp、fp、dp、sp、px 到底是什么

上一篇写 TextButtonToggle 时,代码里已经出现了很多数字:

Text('消息通知')
  .fontSize(18)
  .margin({ top: 6 })

Button('保存设置')
  .width('100%')
  .height(44)

这些数字不是随手写的。它们背后有单位,只是 ArkUI 在很多属性上允许省略单位。刚开始学 HarmonyOS 时,如果你带着 Android 的习惯直接套 dpsppx,很容易把三件事混在一起:

  • 元素尺寸:卡片多宽、按钮多高、内边距多少。
  • 文字尺寸:标题多大、说明文字多大。
  • 真实像素:截图、图片、Canvas、设备屏幕上的物理像素。

这篇先从屏幕和像素讲起,再回到代码里:写页面尺寸时用什么单位,写文字时用什么单位,什么时候才需要关心 px。

先把屏幕上的 px 讲清楚

先看几个买手机、买显示器时经常出现的词:1080p2K4K8K16:920:9

它们说的不是同一件事:

  • 16:920:9 说的是屏幕宽高比例,也就是横向和纵向的比例关系。
  • 1080p2K4K8K 通常说的是分辨率级别,也就是屏幕横向和纵向大约有多少个像素点。
  • px 是物理像素,屏幕最终发光显示出来的一个个点。

例如一块 1920 × 1080 的屏幕,一共有 1920 列、1080 行物理像素;一块 3840 × 2160 的 4K 屏幕,横纵像素数量都更多。问题在于:像素数量变多,不等于屏幕一定变大。手机屏幕可以很小但像素很密,电视屏幕可以很大但观看距离也更远。

这里就引出了 dpidpi 是 dots per inch,意思是“每英寸有多少个点”。严格说,屏幕领域更常见的物理指标是 ppi(pixels per inch,每英寸像素数),但 Android 文档和开发语境里经常用 dpi 来表示屏幕密度。写 UI 时可以先把它理解成:同样一英寸长度里塞进多少个像素点。塞得越多,密度越高,画面越细腻,同样数量的 px 看起来也越小。

举个直观的例子:

同样是 1080 × 1920 像素
5 英寸手机屏:像素挤得更密,dpi 更高
10 英寸平板屏:像素分布得更开,dpi 更低

所以判断界面元素大小时,不能只看分辨率,还要看屏幕物理尺寸和像素密度。px 只告诉你用了多少个像素点,dpi 才说明这些像素点在真实屏幕上挤得有多密。

如果按对角线粗略估算,上面两个屏幕的像素密度差异会非常明显:

1080 × 1920,5 英寸:大约 441 ppi/dpi
1080 × 1920,10 英寸:大约 220 ppi/dpi

同样是 44px,放在 5 英寸手机上只占很短一段物理长度;放在 10 英寸平板上会变长很多。dpi 这个概念要解决的就是:把“像素个数”换算到“真实屏幕上的视觉大小”时,必须知道像素密度。

如果应用直接用 px 写界面,就会碰到一个很尴尬的问题:

同样写 44px
低密度屏幕:看起来还行
高密度屏幕:因为像素太密,看起来会变得很小

UI 布局真正关心的不是“占多少个物理像素”,而是“在人眼看来占多大一块视觉空间”。这就是 Android 演进出 dp / sp,HarmonyOS ArkUI 使用 vp / fp 的原因:开发者写一个相对稳定的视觉单位,系统再根据设备密度、字体设置换算成最终的 px

可以先用一张表把关系放住:

层次 Android HarmonyOS ArkUI 解决的问题
物理像素 px px 屏幕最终显示多少个像素点
布局视觉尺寸 dp vp 不同屏幕密度下,按钮、间距、圆角看起来大小接近
文字视觉尺寸 sp fp 文字既适配屏幕密度,也照顾用户字体大小设置

底层换算可以粗略理解成两步:

布局/文字单位  --乘以设备密度和字体缩放-->  物理像素 px

Android 官方常用的密度换算公式是:

px = dp * (dpi / 160)

其中 160dpi 是 Android 早期定义的基准密度,dpi / 160 可以理解为密度倍率。160dpi 附近的设备上,1dp 大约对应 1px320dpi 设备上,密度倍率约为 21dp 大约对应 2px;到了更高密度的屏幕,1dp 会换算成更多 px。这样同样写 16dp,系统会在高密度屏幕上使用更多物理像素,换来接近的视觉大小。

把数字代进去会更直观:

160dpi:44dp * (160 / 160) = 44px
320dpi:44dp * (320 / 160) = 88px
480dpi:44dp * (480 / 160) = 132px

按钮在高密度屏幕上用了更多物理像素,但开发者写的仍然是 44dp。这就是 dp / vp 这类单位存在的价值:开发者表达视觉尺寸,平台负责换算成最终像素。

spdp 的密度换算基础上,还会叠加用户字体缩放。HarmonyOS 侧的 vpfp 也承担类似职责:vp 负责布局视觉尺寸,fp 负责字体尺寸。日常写页面时,先不要从 px 出发,而是先判断这个数字属于布局、文字,还是渲染结果。

先看一张设置卡片里的单位

还是用上一节的通知设置卡片。把代码里的数字按用途标一下:

@Component
struct NotifySettingCard {
  @State enableNotify: boolean = true

  build() {
    Column() {
      Row() {
        Column() {
          Text('消息通知')
            .fontSize(18) // 文字大小:18fp
            .fontWeight(FontWeight.Medium)
          Text(this.enableNotify ? '已开启,重要消息会及时提醒' : '已关闭,你可能错过重要消息')
            .fontSize(13) // 文字大小:13fp
            .fontColor('#6B7280')
            .margin({ top: 6 }) // 间距:6vp
        }
        .layoutWeight(1)

        Toggle({ type: ToggleType.Switch, isOn: this.enableNotify })
          .onChange((isOn: boolean) => {
            this.enableNotify = isOn
          })
      }
      .width('100%')
      .alignItems(VerticalAlign.Center)

      Button('保存设置')
        .width('100%')
        .height(44) // 高度:44vp
        .margin({ top: 16 }) // 间距:16vp
        .onClick(() => {
          console.info(`save notify setting: ${this.enableNotify}`)
        })
    }
    .width('100%')
    .padding(16) // 内边距:16vp
    .backgroundColor(Color.White)
    .borderRadius(12) // 圆角:12vp
  }
}

在 ArkUI 里,可以先按这个规则记:

  • 宽度、高度、padding、margin、圆角这类布局尺寸,优先理解为 vp
  • fontSize 这类文字尺寸,优先理解为 fp
  • 只有在处理图片原始尺寸、Canvas 绘制、屏幕截图、设备像素密度时,再把 px 拿出来。

这个规则不是为了背概念,而是为了避免最常见的错误:把“元素尺寸”和“文字尺寸”都当成同一种数字。

Android 侧怎么写:dp 管尺寸,sp 管文字

Android 里对应的写法更常见:

@Composable
fun NotifySettingCard() {
  Column(
    modifier = Modifier
      .fillMaxWidth()
      .background(Color.White, RoundedCornerShape(12.dp))
      .padding(16.dp)
  ) {
    Text(
      text = "消息通知",
      fontSize = 18.sp,
      fontWeight = FontWeight.Medium
    )

    Text(
      text = "已开启,重要消息会及时提醒",
      fontSize = 13.sp,
      color = Color(0xFF6B7280),
      modifier = Modifier.padding(top = 6.dp)
    )

    Button(
      onClick = { },
      modifier = Modifier
        .fillMaxWidth()
        .height(44.dp)
        .padding(top = 16.dp)
    ) {
      Text("保存设置")
    }
  }
}

Android 的日常规则可以写得很直白:

  • dp 用于布局尺寸:宽高、间距、圆角、阴影偏移、点击区域。
  • sp 用于文字尺寸:标题、正文、说明文字。
  • px 是屏幕上的真实像素,不建议直接拿来写普通布局。

dp 的核心是屏幕密度无关。回到前面的换算逻辑,同样写 16.dp,在低密度屏幕上会占用较少物理像素,在高密度屏幕上会占用更多物理像素。开发者写的是“视觉上的 16 个密度无关单位”,系统再换算成真实像素。

sp 可以理解为“用于字体的 dp”。它不只考虑屏幕密度,还要考虑用户的字体大小设置。用户把系统字体调大时,sp 文字会跟着变大;普通 dp 间距不会因为字体设置直接变大。

HarmonyOS 侧怎么写:vp 管尺寸,fp 管文字

HarmonyOS ArkUI 里,常见的是 vpfp

Column() {
  Text('消息通知')
    .fontSize(18) // 18fp

  Text('已开启,重要消息会及时提醒')
    .fontSize(13) // 13fp
    .margin({ top: 6 }) // 6vp

  Button('保存设置')
    .height(44) // 44vp
    .margin({ top: 16 }) // 16vp
}
.padding(16) // 16vp
.borderRadius(12) // 12vp

如果你从 Android 过来,可以先建立这组对应关系:

Android dp  ≈ HarmonyOS vp   用于布局尺寸
Android sp  ≈ HarmonyOS fp   用于文字尺寸
Android px  ≈ HarmonyOS px   真实物理像素

这里的“≈”很重要。它不是说两边内部实现完全一样,而是说它们承担的开发职责相近:

  • Android 用 dp 避免布局在不同屏幕密度下忽大忽小。
  • HarmonyOS 用 vp 表达屏幕密度无关的视觉尺寸。
  • Android 用 sp 让文字跟随用户字体设置。
  • HarmonyOS 用 fp 表达字体像素,适合文字大小。

如果只问“布局尺寸”这件事,dpvp 基本可以当成同类概念:它们都不是物理像素,都要按设备密度换算成最终 px;都用来避免 44px 这种写法在高密度屏幕上变得太小。

所以这里的“≈”不要理解成“差异很大,只能勉强类比”。更准确的说法是:对业务 UI 来说,dpvp 的主要差异不是设计目标,而是平台命名与框架默认单位规则不同。

Android 的 dp 明确挂在 Android 的 density 体系上,基准是 160dpi,换算时看的是 Android 运行环境给出的密度倍率。Compose 里通常把这个单位写得很显性:16.dp44.dp

HarmonyOS ArkUI 的 vp 是 ArkUI 的虚拟像素单位,框架以 vp 作为很多尺寸属性的默认单位,再按当前设备/窗口的显示密度换算成物理像素。你写 .padding(16) 时,看起来只是一个数字,但它不是裸 px,而是这个属性默认按 vp 解释。

这组“≈”可以直接读成:

dp 和 vp:都是布局视觉尺寸单位
日常写页面时可以按同类概念理解
区别主要在平台命名、代码写法和默认单位规则

放到代码里,差异主要落在三层:

差异点 Android HarmonyOS ArkUI
代码写法 Compose 里通常显式写 16.dp18.sp ArkUI 很多属性可直接写数字,布局属性默认按 vp,字体属性默认按 fp
类型体系 DpTextUnit 这类类型在代码里更显性 更依赖组件属性语义:.padding(16).fontSize(16) 看起来都是数字,但默认单位不同
额外单位 日常主要是 dpsppx 除了 vpfppx,还常见 lpx,用于按设计稿宽度做比例缩放

因此,dp ≈ vp 的重点不是强调差异,而是建立对应关系:Android 写布局尺寸时优先用 dp,ArkUI 写布局尺寸时优先用 vp 如果你把一个 Android 页面按视觉稿迁到 ArkUI,16dp -> 16vp 通常就是合理的起点;后面再根据目标设备和设计验收微调即可。

所以把 Android UI 搬到 ArkUI 时,不要机械地把 16dp 改成 16px。更接近的写法通常是:

16dp  ->  16vp
18sp  ->  18fp

在 ArkUI 代码里,很多 API 可以直接写数字,所以你看到的是 .padding(16).fontSize(18)。这不代表“没有单位”,而是对应属性有默认单位:布局尺寸按 vp 理解,字体大小按 fp 理解。

px:它不是不能用,而是不该管普通布局

px 是物理像素。手机屏幕最终当然都是由像素显示出来的,但应用代码不应该把普通布局直接绑死在 px 上。

假设你写一个按钮高度:

Button('保存设置')
  .height('44px')

这个按钮在不同屏幕密度上会出现明显差异:同样 44 个物理像素,在高密度屏幕上会显得更小。页面不是按“物理像素个数”设计的,而是按“人眼看到的大小”设计的。

px 更适合这些场景:

  • 处理图片原始宽高,例如一张图片实际是 1080px * 1920px
  • 做 Canvas 或自定义绘制时,需要和像素缓冲区、位图、截图结果对齐。
  • 做性能或渲染诊断时,需要看设备真实像素密度、截图尺寸、GPU 渲染输出。

普通页面里的宽度、高度、边距、圆角,不要优先使用 px

lpx:HarmonyOS 里的设计稿比例单位

ArkUI 里还会看到 lpx。它和 vpfp 的用途不一样。

lpx 更偏向“按设计稿宽度做比例缩放”。例如设计稿以某个宽度作为基准,组件在不同屏幕宽度下按比例换算。这类单位适合某些需要严格按设计稿比例铺开的场景,但不适合所有页面都默认使用。

日常写业务页面时,可以先按这个优先级:

  • 布局尺寸:vp
  • 文字尺寸:fp
  • 特殊设计稿比例:lpx
  • 真实像素/绘制/图片:px

不要把 lpx 当成 vp 的替代品。它解决的是“按设计稿比例缩放”的问题,不是普通布局的默认单位。

一个常见误区:文字也用 vp/dp

有些代码会把所有数字都统一成一个单位:布局用 16,文字也用 16,看起来很整齐,但会带来一个问题:文字没有真正跟随用户字体设置。

Android 里,如果你把文字写成 16.dp,用户调整系统字体大小时,这段文字不会按预期缩放。正确写法是 16.sp

HarmonyOS 里也类似。文字大小应该落在 fp 这条线上,而不是把所有尺寸都当成 vp

可以这样记:

布局是空间问题:用 dp / vp
文字是阅读问题:用 sp / fp
像素是渲染结果:用 px

布局关心的是元素之间的相对空间;文字关心的是用户能不能读清楚;像素关心的是屏幕最终怎么画出来。

什么时候需要做单位换算

大多数 UI 代码不需要手动换算。你只需要写 16.dp18.sp.padding(16).fontSize(18),让系统按设备密度和字体设置去换算。

需要手动换算的情况通常是这些:

  • 你拿到的是一张图片的 px 尺寸,要把它放进布局里。
  • 你在自定义绘制里拿到 Canvas 的物理像素坐标。
  • 你要处理手势坐标、截图坐标、缩放比例。
  • 你在写跨端对照表,需要说明 dp/vppx 的关系。

如果只是写一张设置卡片,不要先写换算工具。先把属性单位用对。

小结表:这些单位该怎么理解

单位 平台 主要用途 可以怎么理解 常见错误
dp Android 布局尺寸 density-independent pixel px 写普通布局
sp Android 文字尺寸 会受用户字体大小影响的字体单位 dp 写文字
vp HarmonyOS / ArkUI 布局尺寸 virtual pixel,类似 Android 的 dp 职责 把 Android 的 dp 改成 px
fp HarmonyOS / ArkUI 文字尺寸 font pixel,类似 Android 的 sp 职责 vp 统一所有文字
px 两端都有 真实物理像素 屏幕最终显示的像素点 直接拿来写按钮高度和间距
lpx HarmonyOS / ArkUI 按设计稿比例缩放 适合特定设计稿比例场景 当成默认布局单位

写 UI 时先分清三类数字:

  • 控件占多少空间:dp / vp
  • 文字多大:sp / fp
  • 屏幕最终多少像素:px

只要这个边界清楚,后面讲基础属性、列表项、输入框、响应式布局时,数字就不会变成一堆“看起来差不多”的魔法值。

参考素材

  • Android:Support different pixel densities:https://developer.android.com/training/multiscreen/screendensities
  • Android:Style text in Compose:https://developer.android.com/develop/ui/compose/text/style-text
  • Android:Material Design scale in Compose:https://developer.android.com/develop/ui/compose/designsystems/material#scale
  • HarmonyOS:像素单位(ArkUI):https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-pixel-units
  • HarmonyOS:像素单位(ArkUI V5):https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-pixel-units-V5
  • HarmonyOS:单位使用说明:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-common-components-units-V5

// Kai@CodeHubble // 观测坐标:Android-HarmonyOS/2026-05-17

上一篇