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 в пиксели по време на изпълнение. Директното умножение по scaledDensity е по-малко надеждно, защото не обработва гранични случаи (нулеви стойности, препълване). 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×, то ще премине основните тестове за достъпност. 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 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също