sp — Scale-independent Pixels, единица измерения текста в Android, учитывающая настройки размера шрифта на устройстве. В отличие от dp, sp масштабируется вместе с системным кеглем, обеспечивая доступность интерфейсов. Статья объясняет разницу между sp и dp, показывает настройку textSize в XML и Kotlin, а также раскрывает лучшие практики работы с типографикой в Android.
Главное
sp (scale-independent pixel) — единица измерения размера шрифта в Android, которая автоматически масштабируется в соответствии с системными настройками пользователя. Когда владелец устройства увеличивает размер шрифта в настройках, все элементы со значениями в sp пропорционально растут. Это обеспечивает доступность интерфейса для людей с ослабленным зрением.
Базовое соотношение: 1 sp = 1 dp при стандартном масштабе шрифта 1.0 (normal). Если пользователь устанавливает шрифт 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 и dp — одна из самых частых тем вопросов на собеседованиях Android-разработчиков и в реальных code review. Обе единицы привязаны к density-independent pixel (160 dpi = 1 dp = 1 px на экране mdpi), но ведут себя по-разному при изменении системного кегля.
| Характеристика | sp | dp |
|---|---|---|
| Назначение | Размер текста | Размеры элементов, отступы, ширина, высота |
| Масштабирование под шрифт | Да (scale factor) | Нет |
| Масштабирование под плотность | Да | Да |
| API для установки | android:textSize | android:layout_width, layout_height, padding, margin |
| Material Design Token | typescale | spacing, sizing |
На практике ошибка — использовать dp для текста. Если текст задан в dp, он не масштабируется при увеличении шрифта, что делает интерфейс недоступным для пользователей с плохим зрением. Обратная ошибка — использовать sp для высоты кнопки или отступов: при увеличении шрифта кнопка может «раздуться», сломав компоновку.
Масштабирование sp реализовано на уровне Android Framework в классе TypedValue. Формула пересчёта: sp = dp × scaledDensityD, где scaledDensityD — масштабный коэффициент, учитывающий как плотность экрана, так и настройку шрифта пользователя.
// Программное получение scaledDensity в Kotlin
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 в пиксели в runtime. Прямое умножение на scaledDensity менее надёжно, так как не обрабатывает edge-кейсы (нулевые значения, переполнение). scaledDensity меняется при изменении настроек шрифта через Settings > Display > Font size.
Android предоставляет пять предустановленных уровней масштаба шрифта: Small (0.85×), Default (1.0×), Large (1.15×), Largest (1.3×) и дополнительный уровень для Very Large (>1.3×) в Android 14+. Производители устройств (Samsung, Xiaomi) добавляют собственные уровни — вплоть до 2.0×. Минимальный размер текста в sp никогда не гарантирует фиксированное количество пикселей.
В XML-разметке sp применяется исключительно в атрибуте android:textSize. Все остальные размеры — width, height, padding, margin, elevation — задаются в dp. Правило простое: если элемент отображает текст — его размер в sp, если элемент определяет геометрию — размер в dp.
<!-- Разметка TextView с textSize в sp -->
<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. Это упрощает поддержку типографики, позволяет переопределять размеры для разных конфигураций экрана и обеспечивает единообразие во всём приложении.
<!-- 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) ухудшают рендеринг из-за субпиксельного позиционирования.
В современной Android-разработке текст редко задаётся напрямую через textSize в sp. Вместо этого используется система MaterialTheme.typography, где каждый стиль предопределён и включает размер, вес, межстрочный интервал и letter-spacing.
// Кастомная типографика через 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
)
)Функция-расширение .sp в Jetpack Compose автоматически транслируется в scale-independent pixels. При изменении системного кегля Compose пересчитывает fontSize, lineHeight и spacing в соответствии с новым scaledDensity. TextUnit (класс, представляющий sp) гарантирует, что текст масштабируется, а отступы — нет.
В Jetpack Compose sp представлен типом TextUnit, который не может быть случайно использован для размеров элементов. Компилятор Kotlin выдаёт ошибку, если передать TextUnit туда, где ожидается Dp. Это устраняет целый класс багов, связанных с неправильным масштабированием.
// TextUnit в Compose — безопасная работа со 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
)
}
}
// Явное задание fontSize в sp
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 — основа доступного Android-приложения. Ниже собраны правила, основанные на рекомендациях Google Material Design и Android Developers Guide.
Главное правило доступности: пользователь должен иметь возможность увеличить шрифт без потери функциональности. Если приложение корректно работает при масштабе шрифта 1.3×, оно пройдёт базовые тесты accessibility. Google Play может отклонить приложение, если textSize задан в dp и интерфейс не масштабируется.
Часто задаваемые вопросы
sp (scale-independent pixel) масштабируется при изменении системного размера шрифта, а dp — нет. sp используется только для текста, dp — для всех остальных размеров элементов. Коэффициент пересчёта: sp = dp × FONT_SCALE.
Нет. Отступы, высота кнопок, ширина карточек должны задаваться в dp. Использование sp для отступов приводит к тому, что при увеличении шрифта элементы растут неестественно и могут выходить за границы экрана.
Откройте Settings > Display > Font size и установите максимальное значение (Largest). Запустите приложение и проверьте, что весь текст читается, элементы не накладываются друг на друга, а кнопки не выходят за границы экрана. Для автоматизации используйте Espresso с UiAutomator.
Material Design рекомендует минимальный размер 11sp для labelSmall. Для основного текста — от 14sp (bodyMedium) до 16sp (bodyLarge). Размеры меньше 11sp не рекомендуются, так как становятся нечитаемыми на устройствах с высокой плотностью пикселей.
Текст будет отображаться в фиксированном размере независимо от системных настроек шрифта. Пользователи с увеличенным шрифтом не смогут комфортно читать такой текст. Это нарушает требования доступности WCAG и может стать причиной отклонения приложения в Google Play.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также