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 (عادي). إذا قام المستخدم بتعيين الخط إلى كبير (1.15×)، يتحول 16 sp إلى 18.4 بكسل فيزيائي. عند حجم الخط الصغير (0.85×)، يصبح نفس 16 sp يساوي 13.6 بكسل. تتم إدارة آلية التحجيم عبر Settings.System.FONT_SCALE على مستوى إطار Android.
وفقًا لإرشادات Google Material Design، يجب تحديد النص حصريًا بـ sp. الاستثناء هو التسميات الثابتة في المكونات حيث يكون الحجم حاسمًا للتخطيط (مثل النص داخل أزرار ذات ارتفاع ثابت). في هذه الحالات، يُوصى باستخدام dp مع التحكم اليدوي في إمكانية الوصول.
الفرق بين sp و dp هو أحد أكثر المواضيع شيوعًا في مقابلات مطوري Android ومراجعات الكود الفعلية. كلتا الوحدتين مرتبطتان بالبكسل المستقل عن الكثافة (160 dpi = 1 dp = 1 بكسل على شاشات mdpi)، لكنهما تتصرفان بشكل مختلف عند تغيير حجم خط النظام.
| الخاصية | sp | dp |
|---|---|---|
| الغرض | حجم النص | أبعاد العناصر، الحشو، العرض، الارتفاع |
| تحجيم الخط | نعم (عامل التحجيم) | لا |
| تحجيم الكثافة | نعم | نعم |
| API الإعداد | android:textSize | android:layout_width, layout_height, padding, margin |
| رمز Material Design | typescale | spacing, sizing |
من الناحية العملية، الخطأ الشائع هو استخدام dp للنص. إذا تم ضبط النص بـ dp، فلن يتم تحجيمه عند زيادة حجم الخط، مما يجعل الواجهة غير قابلة للوصول للمستخدمين ضعاف البصر. الخطأ المعاكس هو استخدام sp لارتفاع الزر أو الحشو: عند زيادة الخط، قد "ينتفخ" الزر مما يكسر التخطيط.
تم تطبيق تحجيم sp على مستوى إطار Android في فئة 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 خمسة مستويات محددة مسبقًا لمقياس الخط: صغير (0.85×)، افتراضي (1.0×)، كبير (1.15×)، أكبر (1.3×) ومستوى إضافي كبير جدًا (>1.3×) في Android 14+. يضيف مصنعو الأجهزة (Samsung, Xiaomi) مستوياتهم الخاصة — حتى 2.0×. لا يضمن الحد الأدنى لحجم النص بـ sp أبدًا عددًا ثابتًا من البكسل.
في تخطيطات XML، يُستخدم sp حصريًا في السمة android:textSize. جميع الأبعاد الأخرى — العرض، الارتفاع، الحشو، الهامش، الارتفاع — تُضبط بـ 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 13 مستوى من الأحجام — من 11sp للتسميات الصغيرة إلى 57sp للعناوين الكبيرة. تستخدم مشاريع Android عادةً 8–10 مستويات. يجب أن تكون جميع القيم مضاعفات 1sp — القيم الكسرية (15.5sp) تضعف العرض بسبب تحديد المواقع دون البكسل.
في تطوير Android الحديث، نادرًا ما يتم ضبط النص مباشرة عبر textSize بـ sp. بدلاً من ذلك، يُستخدم نظام MaterialTheme.typography، حيث يكون كل نمط محددًا مسبقًا ويتضمن الحجم والوزن وتباعد الأسطر وتباعد الأحرف.
// طباعة مخصصة عبر 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 تُترجم تلقائيًا إلى بكسل مستقل عن المقياس. عندما يتغير حجم خط النظام، يعيد 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. إذا كان هناك حاجة لحجم مخصص، يُستخدم القيمة 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. سوف نقدم لك النصح ونقترح أفضل حل.