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-developer و در 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-caseها (مقادیر صفر، سرریز) را مدیریت نمیکند. 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 مطابقت دارند. اگر اندازه سفارشی نیاز است، از literal 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 از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید