sp — Scale-independent Pixels, jednostka miary tekstu w Androidzie, uwzględniająca ustawienia rozmiaru czcionki na urządzeniu. W przeciwieństwie do dp, sp skaluje się wraz z systemowym rozmiarem czcionki, zapewniając dostępność interfejsów. Artykuł wyjaśnia różnicę między sp a dp, pokazuje konfigurację textSize w XML i Kotlin, a także przedstawia najlepsze praktyki pracy z typografią w Androidzie.
Najważniejsze
sp (scale-independent pixel) — jednostka miary rozmiaru czcionki w Androidzie, która automatycznie skaluje się zgodnie z systemowymi ustawieniami użytkownika. Gdy właściciel urządzenia zwiększa rozmiar czcionki w ustawieniach, wszystkie elementy z wartościami w sp proporcjonalnie rosną. Zapewnia to dostępność interfejsu dla osób ze słabym wzrokiem.
Podstawowa zależność: 1 sp = 1 dp przy standardowej skali czcionki 1.0 (normal). Jeśli użytkownik ustawi czcionkę Large (1.15×), 16 sp zamienia się w 18.4 fizycznych piksela. Przy rozmiarze czcionki Small (0.85×) te same 16 sp staną się 13.6 px. Mechanizm skalowania jest zarządzany przez Settings.System.FONT_SCALE na poziomie Android Framework.
Według Google Material Design Guidelines, tekst powinien być ustawiany wyłącznie w sp. Wyjątkiem są stałe etykiety w komponentach, gdzie rozmiar jest krytyczny dla układu (np. tekst wewnątrz przycisków o stałej wysokości). W takich przypadkach zaleca się używanie dp z ręczną kontrolą dostępności.
Różnica między sp a dp — jeden z najczęstszych tematów pytań na rozmowach kwalifikacyjnych dla Android-developerów oraz w rzeczywistych code review. Obie jednostki są powiązane z density-independent pixel (160 dpi = 1 dp = 1 px na ekranie mdpi), ale zachowują się inaczej przy zmianie systemowego rozmiaru czcionki.
| Charakterystyka | sp | dp |
|---|---|---|
| Przeznaczenie | Rozmiar tekstu | Rozmiary elementów, odstępy, szerokość, wysokość |
| Skalowanie względem czcionki | Tak (scale factor) | Nie |
| Skalowanie względem gęstości | Tak | Tak |
| API do ustawienia | android:textSize | android:layout_width, layout_height, padding, margin |
| Material Design Token | typescale | spacing, sizing |
W praktyce błędem jest używanie dp dla tekstu. Jeśli tekst jest ustawiony w dp, nie skaluje się przy zwiększeniu czcionki, co czyni interfejs niedostępnym dla użytkowników ze słabym wzrokiem. Odwrotnym błędem jest używanie sp dla wysokości przycisku lub odstępów: przy zwiększeniu czcionki przycisk może się „rozdąć", psując układ.
Skalowanie sp jest zaimplementowane na poziomie Android Framework w klasie TypedValue. Wzór przeliczeniowy: sp = dp × scaledDensity, gdzie scaledDensity to współczynnik skali uwzględniający zarówno gęstość ekranu, jak i ustawienie czcionki użytkownika.
// Programowe uzyskanie scaledDensity w Kotlin
val scaledDensity = with(context.resources) {
displayMetrics.scaledDensity
}
// Konwersja sp na px
fun spToPx(sp: Float, context: Context): Float {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,
sp,
context.resources.displayMetrics
)
}
// Konwersja px na sp
fun pxToSp(px: Float, context: Context): Float {
return px / context.resources.displayMetrics.scaledDensity
}Metoda TypedValue.applyDimension — jedyny poprawny sposób konwersji sp na piksele w runtime. Bezpośrednie mnożenie przez scaledDensity jest mniej niezawodne, ponieważ nie obsługuje edge-case'ów (wartości zerowe, przepełnienie). scaledDensity zmienia się przy zmianie ustawień czcionki przez Settings > Display > Font size.
Android udostępnia pięć predefiniowanych poziomów skali czcionki: Small (0.85×), Default (1.0×), Large (1.15×), Largest (1.3×) oraz dodatkowy poziom Very Large (>1.3×) w Android 14+. Producenci urządzeń (Samsung, Xiaomi) dodają własne poziomy — aż do 2.0×. Minimalny rozmiar tekstu w sp nigdy nie gwarantuje stałej liczby pikseli.
W XML sp jest używany wyłącznie w atrybucie android:textSize. Wszystkie pozostałe rozmiary — width, height, padding, margin, elevation — ustawia się w dp. Zasada jest prosta: jeśli element wyświetla tekst — jego rozmiar w sp, jeśli element określa geometrię — rozmiar w dp.
<!-- Układ TextView z textSize w 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" />
<!-- Użycie zasobów dimens -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/text_body_large" />Zaleca się wyodrębnianie rozmiarów tekstu do pliku dimens.xml. Ułatwia to utrzymanie typografii, pozwala nadpisywać rozmiary dla różnych konfiguracji ekranu i zapewnia spójność w całej aplikacji.
<!-- 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 zawiera 13 poziomów rozmiarów — od 11sp dla małych etykiet do 57sp dla dużych nagłówków wyświetlanych. Projekty Android zwykle używają 8–10 poziomów. Wszystkie wartości powinny być wielokrotnościami 1sp — wartości ułamkowe (15.5sp) pogarszają renderowanie z powodu subpikselowego pozycjonowania.
W nowoczesnym tworzeniu aplikacji Android tekst rzadko jest ustawiany bezpośrednio przez textSize w sp. Zamiast tego używa się systemu MaterialTheme.typography, gdzie każdy styl jest predefiniowany i zawiera rozmiar, wagę, odstępy między wierszami i letter-spacing.
// Niestandardowa typografia przez 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
)
)Funkcja rozszerzająca .sp w Jetpack Compose automatycznie tłumaczy się na scale-independent pixels. Przy zmianie systemowego rozmiaru czcionki Compose przelicza fontSize, lineHeight i spacing zgodnie z nowym scaledDensity. TextUnit (klasa reprezentująca sp) gwarantuje, że tekst się skaluje, a odstępy — nie.
W Jetpack Compose sp jest reprezentowany przez typ TextUnit, który nie może być przypadkowo użyty do rozmiarów elementów. Kompilator Kotlin zgłasza błąd, jeśli TextUnit zostanie przekazany tam, gdzie oczekiwane jest Dp. Eliminuje to całą klasę błędów związanych z nieprawidłowym skalowaniem.
// TextUnit w Compose — bezpieczna praca ze 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
)
}
}
// Jawne ustawienie fontSize w sp
Text(
text = "Niestandardowy rozmiar",
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.primary
)W Compose nie ma bezpośredniego odpowiednika atrybutu XML android:textSize — rozmiar tekstu ustawia się przez TextStyle.fontSize. System MaterialTheme.typography udostępnia predefiniowane style, które odpowiadają Material Design Type Scale. Jeśli potrzebny jest niestandardowy rozmiar, używa się literału 20.sp — język gwarantuje, że jest to TextUnit, a nie Dp.
Prawidłowe używanie sp — podstawa dostępnej aplikacji Android. Poniżej zebrano zasady oparte na rekomendacjach Google Material Design i Android Developers Guide.
Główna zasada dostępności: użytkownik powinien mieć możliwość zwiększenia czcionki bez utraty funkcjonalności. Jeśli aplikacja działa poprawnie przy skali czcionki 1.3×, przejdzie podstawowe testy accessibility. Google Play może odrzucić aplikację, jeśli textSize jest ustawiony w dp i interfejs nie skaluje się.
Często zadawane pytania
sp (scale-independent pixel) skaluje się przy zmianie systemowego rozmiaru czcionki, a dp — nie. sp jest używane tylko dla tekstu, dp — dla wszystkich pozostałych rozmiarów elementów. Współczynnik przeliczeniowy: sp = dp × FONT_SCALE.
Nie. Odstępy, wysokość przycisków, szerokość kart powinny być ustawiane w dp. Używanie sp do odstępów powoduje, że przy zwiększeniu czcionki elementy rosną nienaturalnie i mogą wychodzić poza granice ekranu.
Otwórz Settings > Display > Font size i ustaw maksymalną wartość (Largest). Uruchom aplikację i sprawdź, czy cały tekst jest czytelny, elementy nie nakładają się na siebie, a przyciski nie wychodzą poza granice ekranu. Do automatyzacji użyj Espresso z UiAutomator.
Material Design zaleca minimalny rozmiar 11sp dla labelSmall. Dla głównego tekstu — od 14sp (bodyMedium) do 16sp (bodyLarge). Rozmiary mniejsze niż 11sp nie są zalecane, ponieważ stają się nieczytelne na urządzeniach z wysoką gęstością pikseli.
Tekst będzie wyświetlany w stałym rozmiarze niezależnie od systemowych ustawień czcionki. Użytkownicy z powiększoną czcionką nie będą mogli komfortowo czytać takiego tekstu. Narusza to wymagania dostępności WCAG i może być powodem odrzucenia aplikacji w Google Play.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również