sp:什么是 Android 中可缩放的文本像素

作者: IT Sectr 发布日期: 2026-02-25 阅读时间: 9 分钟

sp — Scale-independent Pixels,Android 中文本的度量单位,考虑了设备上的字体大小设置。与 dp 不同,sp 会随系统字体大小一起缩放,确保界面的可访问性。文章解释了 sp 和 dp 之间的区别,展示了 XML 和 Kotlin 中的 textSize 配置,并揭示了 Android 中排版的最佳实践。

要点

  • sp — 文本单位,可根据用户系统字体设置缩放
  • dp — 元素尺寸单位,不响应字体大小变化
  • sp 和 dp 的区别:sp = dp × scaleFactor,其中 scaleFactor 取决于字体设置
  • XML 中的 textSize 通过 sp 设置:android:textSize="16sp"
  • Material Design 建议标题的排版步长为 12sp 到 34sp

Android 中 sp 是什么?

sp (scale-independent pixel) — Android 中字体大小的度量单位,根据用户的系统设置自动缩放。当设备所有者在设置中增大字体大小时,所有具有 sp 值的元素会按比例增大。这确保了视力较弱人群的界面可访问性

基本比例:在标准字体缩放 1.0(normal)下,1 sp = 1 dp。如果用户设置 Large (1.15×) 字体,16 sp 将变为 18.4 物理像素。在 Small (0.85×) 字体大小下,同样的 16 sp 将变为 13.6 px。缩放机制通过 Settings.System.FONT_SCALE 在 Android Framework 级别管理。

根据 Google Material Design Guidelines,文本应完全以 sp 设置。例外情况——在尺寸对布局至关重要的组件中的固定标签(例如,固定高度按钮内的文本)。在这种情况下,建议使用 dp 并手动控制可访问性。

sp vs dp:主要区别

spdp 之间的区别——Android 开发者面试和实际 code review 中最常见的问题之一。两个单位都绑定到 density-independent pixel(160 dpi = 1 dp = 1 px 在 mdpi 屏幕上),但在系统字体大小变化时表现不同。

特性spdp
用途文本大小元素尺寸、间距、宽度、高度
随字体缩放是(scale factor)
随密度缩放
设置 APIandroid:textSizeandroid:layout_width, layout_height, padding, margin
Material Design Tokentypescalespacing, sizing

在实践中,错误——对文本使用 dp。如果文本以 dp 设置,字体增大时不会缩放,使界面对视障用户不可访问。相反的错误——对按钮高度或间距使用 sp:字体增大时,按钮可能会"膨胀",破坏布局。

sp 缩放如何工作

sp 缩放是在 Android Framework 级别的 TypedValue 类中实现的。换算公式:sp = dp × scaledDensity,其中 scaledDensity 是同时考虑屏幕密度和用户字体设置的缩放系数。

kotlin
// 在 Kotlin 中以编程方式获取 scaledDensity
val scaledDensity = with(context.resources) {
    displayMetrics.scaledDensity
}

// 将 sp 转换为 px
fun spToPx(sp: Float, context: Context): Float {
    return TypedValue.applyDimension(
        TypedValue.COMPLEX_UNIT_SP,
        sp,
        context.resources.displayMetrics
    )
}

// 将 px 转换为 sp
fun pxToSp(px: Float, context: Context): Float {
    return px / context.resources.displayMetrics.scaledDensity
}

TypedValue.applyDimension 方法——在运行时将 sp 转换为像素的唯一正确方法。直接乘以 scaledDensity 不太可靠,因为它不处理边缘情况(零值、溢出)。scaledDensity 在通过 Settings > Display > Font size 更改字体设置时发生变化。

缩放系数

Android 提供五个预设的字体缩放级别:Small (0.85×)、Default (1.0×)、Large (1.15×)、Largest (1.3×) 以及 Android 14+ 中的额外级别 Very Large (>1.3×)。设备制造商(Samsung、Xiaomi)会添加自己的级别——最高可达 2.0×。sp 中的最小文本大小永远不保证固定的像素数。

在 XML 中使用 sp

在 XML 中,sp 仅用于 android:textSize 属性。所有其他尺寸——width、height、padding、margin、elevation——都以 dp 设置。规则很简单:如果元素显示文本——其大小用 sp,如果元素定义几何形状——大小用 dp。

XML 中 textSize 的基本示例

xml
<!-- 带有 textSize sp 的 TextView 布局 -->
<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/hello"
    android:textSize="16sp"
    android:padding="12dp"
    android:lineSpacingExtra="4dp" />

<!-- 使用 dimens 资源 -->
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="@dimen/text_body_large" />

建议将文本尺寸提取到 dimens.xml 文件中。这简化了排版的维护,允许为不同的屏幕配置覆盖尺寸,并确保整个应用程序的一致性。

带有排版 token 的 dimens.xml

xml
<!-- res/values/dimens.xml -->
<resources>
    <!-- Material Design Type Scale -->
    <dimen name="text_display_large">34sp</dimen>
    <dimen name="text_display_medium">28sp</dimen>
    <dimen name="text_headline_large">24sp</dimen>
    <dimen name="text_headline_medium">20sp</dimen>
    <dimen name="text_title_large">18sp</dimen>
    <dimen name="text_body_large">16sp</dimen>
    <dimen name="text_body_medium">14sp</dimen>
    <dimen name="text_label_large">14sp</dimen>
    <dimen name="text_label_small">11sp</dimen>
</resources>

Material Design Type Scale 包含 13 个尺寸级别——从用于小标签的 11sp 到用于大显示标题的 57sp。Android 项目通常使用 8–10 个级别。所有值应为 1sp 的倍数——小数值 (15.5sp) 会因亚像素定位而降低渲染效果。

通过 Kotlin 配置排版

在现代 Android 开发中,很少直接通过 sp 中的 textSize 设置文本。取而代之的是使用 MaterialTheme.typography 系统,其中每个样式都是预定义的,包括大小、粗细、行间距和字间距。

kotlin
// 通过 Typography API 实现自定义排版
val AppTypography = Typography(
    displayLarge = TextStyle(
        fontWeight = FontWeight.Normal,
        fontSize = 34.sp,
        lineHeight = 40.sp,
        letterSpacing = 0.sp
    ),
    headlineLarge = TextStyle(
        fontWeight = FontWeight.SemiBold,
        fontSize = 24.sp,
        lineHeight = 32.sp
    ),
    titleLarge = TextStyle(
        fontWeight = FontWeight.Medium,
        fontSize = 18.sp,
        lineHeight = 24.sp
    ),
    bodyLarge = TextStyle(
        fontWeight = FontWeight.Normal,
        fontSize = 16.sp,
        lineHeight = 24.sp,
        letterSpacing = 0.5.sp
    ),
    labelSmall = TextStyle(
        fontWeight = FontWeight.Medium,
        fontSize = 11.sp,
        lineHeight = 16.sp,
        letterSpacing = 0.5.sp
    )
)

Jetpack Compose 中的 .sp 扩展函数会自动转换为 scale-independent pixels。当系统字体大小变化时,Compose 会根据新的 scaledDensity 重新计算 fontSize、lineHeight 和 spacing。TextUnit(表示 sp 的类)确保文本缩放,而间距则不缩放。

Jetpack Compose 中的 sp

Jetpack Compose 中,sp 由 TextUnit 类型表示,不能意外用于元素尺寸。Kotlin 编译器会在 TextUnit 被传递到预期为 Dp 的位置时报告错误。这消除了与错误缩放相关的一整类错误。

kotlin
// Compose 中的 TextUnit — 安全使用 sp
@Composable
fun ArticleCard(title: String, description: String) {
    Card(modifier = Modifier
        .fillMaxWidth()
        .padding(16.dp)
    ) {
        Text(
            text = title,
            style = MaterialTheme.typography.titleLarge
        )
        Spacer(modifier = Modifier.height(8.dp))
        Text(
            text = description,
            style = MaterialTheme.typography.bodyMedium
        )
    }
}

// 在 sp 中显式设置 fontSize
Text(
    text = "自定义大小",
    fontSize = 20.sp,
    fontWeight = FontWeight.Bold,
    color = MaterialTheme.colorScheme.primary
)

Compose 中没有 XML 属性 android:textSize 的直接等效项——文本大小通过 TextStyle.fontSize 设置。MaterialTheme.typography 系统提供与 Material Design Type Scale 对应的预定义样式。如果需要自定义大小,则使用字面量 20.sp——语言保证这是 TextUnit,而不是 Dp。

使用 sp 的最佳实践

正确使用 sp 是可访问 Android 应用程序的基础。以下是根据 Google Material Design 和 Android Developers Guide 的建议收集的规则。

  • 始终对文本使用 sp——切勿以 dp 或 px 设置 textSize,否则字体放大的用户将无法阅读界面
  • 对容器使用 dp——按钮高度、间距和卡片宽度应为 dp,否则在大字体下元素可能会重叠
  • 将尺寸提取到 dimens.xml——集中存储排版 token 可简化重构和深色主题支持
  • 不要使用小于 12sp 的 sp——较小尺寸的文本在高 PPI 设备和标准字体下变得不可读
  • 在最大字体缩放下测试——在设备设置中启用最大字体并检查界面是否损坏

可访问性的主要规则:用户应该能够在不损失功能的情况下增大字体。如果应用程序在 1.3× 字体缩放下正常工作,它将通过基本的可访问性测试。如果 textSize 以 dp 设置且界面不缩放,Google Play 可能会拒绝该应用程序。

常见问题

sp 和 dp 有什么区别?

sp (scale-independent pixel) 在系统字体大小变化时缩放,而 dp 则不会。sp 仅用于文本,dp 用于所有其他元素尺寸。换算系数:sp = dp × FONT_SCALE。

sp 可以用于间距吗?

不可以。间距、按钮高度、卡片宽度应以 dp 设置。对间距使用 sp 会导致字体增大时元素不自然地增长,并可能超出屏幕边界。

如何检查应用程序是否与 sp 正常工作?

打开 Settings > Display > Font size 并设置最大值 (Largest)。启动应用程序并检查所有文本是否可读、元素是否重叠、按钮是否超出屏幕边界。对于自动化,请使用 Espresso 和 UiAutomator。

sp 中允许的最小文本大小是多少?

Material Design 建议 labelSmall 的最小尺寸为 11sp。对于正文——从 14sp (bodyMedium) 到 16sp (bodyLarge)。不建议使用小于 11sp 的尺寸,因为在像素密度高的设备上会变得不可读。

如果我用 dp 设置 textSize 会发生什么?

文本将以固定大小显示,不受系统字体设置影响。字体放大的用户将无法舒适地阅读此类文本。这违反了 WCAG 可访问性要求,并可能导致应用程序在 Google Play 中被拒绝。

总结

  • sp — Android 中的字体大小度量单位,通过 FONT_SCALE 根据用户设置缩放
  • dp — 元素尺寸单位,不响应字体大小变化
  • textSize 在 XML 中通过 android:textSize="16sp" 设置,所有其他尺寸——以 dp
  • MaterialTheme.typography 在 Jetpack Compose 中使用 TextUnit (.sp) 实现类型安全
  • dimens.xml — 排版 token 的集中存储,确保一致性
  • 最小文本大小——11sp,建议正文——14–16sp
  • 测试 在最大字体缩放下——可访问性检查的强制阶段

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读