استخدام الخطوط

توضّح هذه الصفحة كيفية ضبط الخطوط في تطبيق Compose.

ضبط الخط

تحتوي الدالة Text على مَعلمة fontFamily للسماح بضبط الخط المستخدَم في الدالة المركّبة. يتم تلقائيًا تضمين مجموعات الخطوط serif وsans-serif وmonospace وcursive:

@Composable
fun DifferentFonts() {
    Column {
        Text("Hello World", fontFamily = FontFamily.Serif)
        Text("Hello World", fontFamily = FontFamily.SansSerif)
    }
}

الكلمات

يمكنك استخدام السمة fontFamily للتعامل مع الخطوط المخصّصة والمحارف المحدّدة في المجلد res/font:

رسم توضيحي لمجلد res > font في بيئة التطوير

يوضّح هذا المثال كيفية تحديد fontFamily استنادًا إلى ملفات الخطوط هذه وباستخدام الدالة Font:

val firaSansFamily = FontFamily(
    Font(R.font.firasans_light, FontWeight.Light),
    Font(R.font.firasans_regular, FontWeight.Normal),
    Font(R.font.firasans_italic, FontWeight.Normal, FontStyle.Italic),
    Font(R.font.firasans_medium, FontWeight.Medium),
    Font(R.font.firasans_bold, FontWeight.Bold)
)

يمكنك تمرير هذه السمة fontFamily إلى الدالة المركّبة Text. بما أنّ fontFamily يمكن أن يتضمّن أوزانًا مختلفة، يمكنك ضبط fontWeight يدويًا لاختيار الوزن المناسب للنص:

Column {
    Text(text = "text", fontFamily = firaSansFamily, fontWeight = FontWeight.Light)
    Text(text = "text", fontFamily = firaSansFamily, fontWeight = FontWeight.Normal)
    Text(
        text = "text",
        fontFamily = firaSansFamily,
        fontWeight = FontWeight.Normal,
        fontStyle = FontStyle.Italic
    )
    Text(text = "text", fontFamily = firaSansFamily, fontWeight = FontWeight.Medium)
    Text(text = "text", fontFamily = firaSansFamily, fontWeight = FontWeight.Bold)
}

الكلمات

للتعرّف على كيفية ضبط أسلوب الخط في تطبيقك بالكامل، يُرجى الاطّلاع على أنظمة التصميم المخصّصة في Compose.

الخطوط القابلة للتنزيل

بدءًا من الإصدار 1.2.0 من Compose، يمكنك استخدام واجهة برمجة التطبيقات للخطوط القابلة للتنزيل في تطبيق Compose لتنزيل خطوط Google بشكل غير متزامن واستخدامها في تطبيقك.

لا يتوفّر حاليًا دعم للخطوط القابلة للتنزيل التي يوفّرها مقدّمو الخدمات المخصّصة.

استخدام الخطوط القابلة للتنزيل آليًا

لتنزيل خط برمجيًا من داخل تطبيقك، اتّبِع الخطوات التالية:

  1. أضِف التبعية:

    أنيق

    dependencies {
        ...
        implementation "androidx.compose.ui:ui-text-google-fonts:1.12.1"
    }

    Kotlin

    dependencies {
        ...
        implementation("androidx.compose.ui:ui-text-google-fonts:1.12.1")
    }
  2. ابدأ تشغيل GoogleFont.Provider باستخدام بيانات اعتماد Google Fonts:
    val provider = GoogleFont.Provider(
        providerAuthority = "com.google.android.gms.fonts",
        providerPackage = "com.google.android.gms",
        certificates = R.array.com_google_android_gms_fonts_certs
    )
    المَعلمات التي يتلقّاها مقدّم الخدمة هي:
    • تمثّل هذه السياسة مرجع موفّر الخطوط في Google Fonts.
    • حزمة موفّر الخطوط لإثبات هوية الموفّر
    • قائمة بمجموعات من تجزئات الشهادات للتحقّق من هوية مقدّم الخدمة. يمكنك العثور على التجزئات المطلوبة لموفّر Google Fonts في ملف font_certs.xml في نموذج تطبيق Jetchat.
  3. تحديد FontFamily:
    // ...
     import androidx.compose.ui.text.googlefonts.GoogleFont
     import androidx.compose.ui.text.font.FontFamily
     import androidx.compose.ui.text.googlefonts.Font
     // ...
    
    val fontName = GoogleFont("Lobster Two")
    
    val fontFamily = FontFamily(
        Font(googleFont = fontName, fontProvider = provider)
    )
    يمكنك الاستعلام عن مَعلمات أخرى للخط، مثل السُمك والنمط، باستخدام FontWeight وFontStyle على التوالي:
    // ...
     import androidx.compose.ui.text.googlefonts.GoogleFont
     import androidx.compose.ui.text.font.FontFamily
     import androidx.compose.ui.text.googlefonts.Font
     // ...
    
    val fontName = GoogleFont("Lobster Two")
    
    val fontFamily = FontFamily(
        Font(
            googleFont = fontName,
            fontProvider = provider,
            weight = FontWeight.Bold,
            style = FontStyle.Italic
        )
    )
  4. اضبط FontFamily لاستخدامه في الدالة المركّبة Text:

Text(
    fontFamily = fontFamily, text = "Hello World!"
)

يمكنك أيضًا تحديد الخطوط لاستخدام FontFamily:

val MyTypography = Typography(
    bodyMedium = TextStyle(
        fontFamily = fontFamily, fontWeight = FontWeight.Normal, fontSize = 12.sp/*...*/
    ),
    bodyLarge = TextStyle(
        fontFamily = fontFamily,
        fontWeight = FontWeight.Bold,
        letterSpacing = 2.sp,
        /*...*/
    ),
    headlineMedium = TextStyle(
        fontFamily = fontFamily, fontWeight = FontWeight.SemiBold/*...*/
    ),
    /*...*/
)

بعد ذلك، اضبط "أسلوب الخط" (Typography) على مظهر تطبيقك:

MyAppTheme(
    typography = MyTypography
)/*...*/

للاطّلاع على مثال لتطبيق يستخدم الخطوط القابلة للتنزيل في Compose مع Material3، يمكنك الاطّلاع على نموذج تطبيق Jetchat.

إضافة خطوط احتياطية

يمكنك تحديد سلسلة من الخطوط الاحتياطية لخطك في حالة فشل تنزيل الخط بشكل صحيح. على سبيل المثال، إذا كان لديك خط قابل للتنزيل محدّد على النحو التالي:

// ...
 import androidx.compose.ui.text.googlefonts.Font
 // ...

val fontName = GoogleFont("Lobster Two")

val fontFamily = FontFamily(
    Font(googleFont = fontName, fontProvider = provider),
    Font(googleFont = fontName, fontProvider = provider, weight = FontWeight.Bold)
)

يمكنك تحديد الإعدادات التلقائية للخط لكل من السُمكين على النحو التالي:

// ...
 import androidx.compose.ui.text.font.Font
 import androidx.compose.ui.text.googlefonts.Font
 // ...

val fontName = GoogleFont("Lobster Two")

val fontFamily = FontFamily(
    Font(googleFont = fontName, fontProvider = provider),
    Font(resId = R.font.my_font_regular),
    Font(googleFont = fontName, fontProvider = provider, weight = FontWeight.Bold),
    Font(resId = R.font.my_font_regular_bold, weight = FontWeight.Bold)
)

تأكَّد من إضافة عمليات الاستيراد الصحيحة.

يؤدي تحديد FontFamily بهذه الطريقة إلى إنشاء FontFamily يحتوي على سلسلتين، واحدة لكل وزن. ستحاول آلية التحميل حلّ مشكلة الخط على الإنترنت أولاً، ثم الخط الموجود في مجلد موارد R.font المحلي.

تصحيح أخطاء عملية التنفيذ

للمساعدة في التحقّق من تنزيل الخط بشكل صحيح، يمكنك تحديد معالج دالة coroutine لتصحيح الأخطاء. يوفّر المعالج سلوك ما يجب فعله في حالة فشل تحميل الخط بشكل غير متزامن.

ابدأ بإنشاء CoroutineExceptionHandler:

val handler = CoroutineExceptionHandler { _, throwable ->
    // process the Throwable
    Log.e(TAG, "There has been an issue: ", throwable)
}

مرِّرها إلى الطريقة createFontFamilyResolver ليستخدم المحلِّل المعالج الجديد:

CompositionLocalProvider(
    LocalFontFamilyResolver provides createFontFamilyResolver(LocalContext.current, handler)
) {
    Column {
        Text(
            text = "Hello World!", style = MaterialTheme.typography.bodyMedium
        )
    }
}

يمكنك أيضًا استخدام واجهة برمجة التطبيقات isAvailableOnDevice من الموفِّر لاختبار ما إذا كان الموفِّر متاحًا وتم ضبط الشهادات بشكل صحيح. لإجراء ذلك، يمكنك استدعاء طريقة isAvailableOnDevice التي تعرض القيمة "خطأ" إذا تم ضبط الموفّر بشكل غير صحيح.

val context = LocalContext.current
LaunchedEffect(Unit) {
    if (provider.isAvailableOnDevice(context)) {
        Log.d(TAG, "Success!")
    }
}

المحاذير

يستغرق Google Fonts عدة أشهر لإتاحة الخطوط الجديدة على Android. هناك فارق زمني بين وقت إضافة خط في fonts.google.com ووقت توفّره من خلال واجهة برمجة التطبيقات للخطوط القابلة للتنزيل (إما في نظام العرض أو في Compose). قد يتعذّر تحميل الخطوط المُضافة حديثًا في تطبيقك مع ظهور الخطأ IllegalStateException. لمساعدة المطوّرين في تحديد هذا الخطأ من بين الأنواع الأخرى من أخطاء تحميل الخطوط، أضفنا رسائل وصفية للاستثناء في Compose مع التغييرات الموضّحة هنا. إذا واجهت أي مشاكل، يُرجى الإبلاغ عنها باستخدام أداة تتبُّع المشاكل.

استخدام الخطوط المتغيّرة

الخط المتغيّر هو تنسيق خط يسمح لملف خط واحد باحتواء أنماط مختلفة. باستخدام الخطوط المتغيّرة، يمكنك تعديل المحاور (أو المَعلمات) لإنشاء النمط المفضّل لديك. يمكن أن تكون هذه المحاور عادية، مثل الوزن والعرض والميل والخط المائل، أو مخصّصة، وتختلف باختلاف الخطوط المتغيّرة.

خمسة إعدادات للخط المتغيّر نفسه بقيم محاور مختلفة
الشكل 1. نص يستخدم خط المتغيّر نفسه المخصّص بقيم محاور مختلفة

يتيح لك استخدام الخطوط المتغيّرة بدلاً من ملفات الخطوط العادية الحصول على ملف خط واحد فقط بدلاً من ملفات متعددة.

لمزيد من المعلومات حول الخطوط المتغيّرة، يُرجى الاطّلاع على Google Fonts Knowledge والكتالوج الكامل للخطوط المتغيّرة المتاحة وجدول المحاور المتوافقة لكل خط.

يوضّح لك هذا المستند كيفية تنفيذ خط متغيّر في تطبيق Compose.

تحميل خط متغيّر

  1. نزِّل الخط المتغيّر الذي تريد استخدامه (على سبيل المثال، Roboto Flex) وضَعه في المجلد app/res/font في تطبيقك. تأكَّد من أنّأنّ ملف ttf الذي تضيفه هو إصدار الخط المتغيّر للخط، وأنّ اسم ملف الخط مكتوب بأحرف صغيرة ولا يحتوي على أي رموز خاصة.

  2. لتحميل خط متغيّر، حدِّد FontFamily باستخدام الخط الموضوع في دليل res/font/:

    // In Typography.kt
    @OptIn(ExperimentalTextApi::class)
    val displayLargeFontFamily =
        FontFamily(
            Font(
                R.font.robotoflex_variable,
                variationSettings = FontVariation.Settings(
                    FontVariation.weight(950),
                    FontVariation.width(30f),
                    FontVariation.slant(-6f),
                )
            )
        )

    تتيح لك واجهة برمجة التطبيقات FontVariation ضبط محاور الخطوط العادية، مثل الوزن والعرض والميل. هذه هي المحاور القياسية المتوفرة مع أي خط متغير. يمكنك إنشاء تكوينات مختلفة للخط بناءً على مكان استخدام الخط.

  3. لا تتوفّر الخطوط المتغيّرة إلا في الإصدار Android O والإصدارات الأحدث، لذا أضِف حاجز حماية واضبط خيارًا احتياطيًا مناسبًا:

    // In Typography.kt
    val default = FontFamily(
        /*
        * This can be any font that makes sense
        */
        Font(
            R.font.robotoflex_static_regular
        )
    )
    @OptIn(ExperimentalTextApi::class)
    val displayLargeFontFamily = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        FontFamily(
            Font(
                R.font.robotoflex_variable,
                variationSettings = FontVariation.Settings(
                    FontVariation.weight(950),
                    FontVariation.width(30f),
                    FontVariation.slant(-6f),
                )
            )
        )
    } else {
        default
    }

  4. استخرِج الإعدادات في مجموعة من الثوابت لتسهيل إعادة استخدامها واستبدال إعدادات الخط بهذه الثوابت:

    // VariableFontDimension.kt
    object DisplayLargeVFConfig {
        const val WEIGHT = 950
        const val WIDTH = 30f
        const val SLANT = -6f
        const val ASCENDER_HEIGHT = 800f
        const val COUNTER_WIDTH = 500
    }
    
    @OptIn(ExperimentalTextApi::class)
    val displayLargeFontFamily = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        FontFamily(
            Font(
                R.font.robotoflex_variable,
                variationSettings = FontVariation.Settings(
                    FontVariation.weight(DisplayLargeVFConfig.WEIGHT),
                    FontVariation.width(DisplayLargeVFConfig.WIDTH),
                    FontVariation.slant(DisplayLargeVFConfig.SLANT),
                )
            )
        )
    } else {
        default
    }

  5. اضبط أسلوب الخط في التصميم المتعدد الأبعاد 3 لاستخدام FontFamily:

    // Type.kt
    val Typography = Typography(
        displayLarge = TextStyle(
            fontFamily = displayLargeFontFamily,
            fontSize = 50.sp,
            lineHeight = 64.sp,
            letterSpacing = 0.sp,
            /***/
        )
    )

    يستخدم هذا النموذج displayLarge أسلوب الخطوط في Material 3، والذي يتضمّن إعدادات مختلفة للخطوط التلقائية والاستخدامات المقترَحة. على سبيل المثال، يجب استخدام displayLarge للنصوص القصيرة والمهمة، لأنّه أكبر نص على الشاشة.

    باستخدام Material 3، يمكنك تغيير القيم التلقائية لـ TextStyle وfontFamily لتخصيص أسلوب الخط. في المقتطف أعلاه، يمكنك ضبط مثيلات TextStyle لتخصيص إعدادات الخط لكل مجموعة خطوط.

  6. بعد تحديد أسلوب الخط، يمكنك تمريره إلى M3 MaterialTheme:

    MaterialTheme(
        colorScheme = MaterialTheme.colorScheme,
        typography = Typography,
        content = content
    )

  7. أخيرًا، استخدِم دالة Text مركّبة وحدِّد النمط على أحد أنماط الخطوط المحدّدة، MaterialTheme.typography.displayLarge:

    @Composable
    @Preview
    fun CardDetails() {
        MyCustomTheme {
            Card(
                shape = RoundedCornerShape(8.dp),
                elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(16.dp)
            ) {
                Column(
                    modifier = Modifier.padding(16.dp)
                ) {
                    Text(
                        text = "Compose",
                        style = MaterialTheme.typography.displayLarge,
                        modifier = Modifier.padding(bottom = 8.dp),
                        maxLines = 1
                    )
                    Text(
                        text = "Beautiful UIs on Android",
                        style = MaterialTheme.typography.headlineMedium,
                        modifier = Modifier.padding(bottom = 8.dp),
                        maxLines = 2
                    )
                    Text(
                        text = "Jetpack Compose is Android’s recommended modern toolkit for building native UI. It simplifies and accelerates UI development on Android. Quickly bring your app to life with less code, powerful tools, and intuitive Kotlin APIs.",
                        style = MaterialTheme.typography.bodyLarge,
                        modifier = Modifier.padding(bottom = 8.dp),
                        maxLines = 3
                    )
                }
            }
        }
    }

    يتم ضبط كل دالة مركّبة من Text من خلال نمط تخصيص التصميم المتعدد الأبعاد، وتحتوي على إعدادات مختلفة للخطوط المتغيّرة. يمكنك استخدام MaterialTheme.typography لاسترداد أسلوب الخط المقدَّم إلى الدالة المركّبة MaterialTheme في M3.

ثلاثة نصوص مختلفة، تعرض جميعها إعدادات خطوط مختلفة.
الشكل 2. خط متغيّر مطبَّق في ثلاثة إعدادات مختلفة.

استخدام محاور مخصّصة

يمكن أن تحتوي الخطوط أيضًا على محاور مخصّصة. ويتم تحديدها ضمن ملف الخط نفسه. على سبيل المثال، يتضمّن خط Roboto Flex محور ارتفاع الجزء الصاعد ("YTAS") الذي يضبط ارتفاع الأجزاء الصاعدة في الأحرف الصغيرة، بينما يضبط عرض الفراغ الداخلي ("XTRA") عرض كل حرف.

يمكنك تغيير قيمة هذه المحاور من خلال إعدادات FontVariation.

لمزيد من المعلومات حول المحاور المخصّصة التي يمكنك ضبطها لخط معيّن، يُرجى الاطّلاع على جدول المحاور المتوافقة لكل خط.

  1. لاستخدام المحاور المخصّصة، حدِّد دوالّ للمحورَين المخصّصَين ascenderHeight وcounterWidth:

    fun ascenderHeight(ascenderHeight: Float): FontVariation.Setting {
        require(ascenderHeight in 649f..854f) { "'Ascender Height' must be in 649f..854f" }
        return FontVariation.Setting("YTAS", ascenderHeight)
    }
    
    fun counterWidth(counterWidth: Int): FontVariation.Setting {
        require(counterWidth in 323..603) { "'Counter width' must be in 323..603" }
        return FontVariation.Setting("XTRA", counterWidth.toFloat())
    }

    تؤدي هذه الدوالّ ما يلي:

    • حدِّد قيودًا للقيم التي يمكن قبولها. كما ترى في دليل الخطوط المتغيّرة، الحد الأدنى لقيمة ascenderHeight (YTAS) هو 649f والحد الأقصى هو 854f.
    • أعِد إعداد الخط، حتى يصبح الإعداد جاهزًا للإضافة إلى الخط. في طريقة FontVariation.Setting()، يتم ترميز اسم المحور (YTAS, XTRA) بشكل ثابت، ويتم استخدام القيمة كمَعلمة.
  2. باستخدام المحاور مع إعدادات الخط، يمكنك تمرير مَعلمات إضافية إلى كل Font يتم تحميله:

    @OptIn(ExperimentalTextApi::class)
    val displayLargeFontFamily = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        FontFamily(
            Font(
                R.font.robotoflex_variable,
                variationSettings = FontVariation.Settings(
                    FontVariation.weight(DisplayLargeVFConfig.WEIGHT),
                    FontVariation.width(DisplayLargeVFConfig.WIDTH),
                    FontVariation.slant(DisplayLargeVFConfig.SLANT),
                    ascenderHeight(DisplayLargeVFConfig.ASCENDER_HEIGHT),
                    counterWidth(DisplayLargeVFConfig.COUNTER_WIDTH)
                )
            )
        )
    } else {
        default
    }

    لاحظ أنّ ارتفاع الحروف الصاعدة الصغيرة قد زاد الآن، وأنّ النص الآخر أصبح أوسع:

ثلاثة نصوص مختلفة تعرض إعدادات مختلفة للخطوط المتغيّرة، مع ضبط محاور مخصّصة، وبعضها يحتوي على أحرف صاعدة صغيرة أعلى وأعرض من ذي قبل.
الشكل 3. نص يعرض المحاور المخصّصة التي تم ضبطها على الخطوط المتغيّرة

مراجع إضافية

لمزيد من المعلومات، يُرجى الاطّلاع على منشور المدوّنة التالي حول الخطوط المتغيّرة: