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 × scaledDensity, де scaledDensity — масштабний коефіцієнт, що враховує як щільність екрану, так і налаштування шрифту користувача.
// Програмне отримання 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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.