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

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

ضبط الخط

تتضمن السمة Text المعلَمة fontFamily للسماح بضبط الخط المستخدَم في العنصر القابل للإنشاء. بشكل افتراضي، يتم تضمين مجموعات الخطوط serif وsans-serif وsolarmora وعائلات الخطوط التدوينية:

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

الكلمات

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

تصوير رسومي لمجلد الخط res > في بيئة التطوير

يوضّح هذا المثال كيف يمكنك تحديد 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.

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

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

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

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

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

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

    رائع

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

    Kotlin

    dependencies {
        ...
        implementation("androidx.compose.ui:ui-text-google-fonts:1.6.8")
    }
  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.
    • حزمة موفر الخط للتحقق من هوية الموفر.
    • تمثّل هذه السمة قائمة بمجموعات علامات التجزئة للشهادات بهدف إثبات هوية الموفّر. يمكنك العثور على علامات التجزئة المطلوبة لموفّر خطوط Google في ملف 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(
    fontFamily = fontFamily, text = "Hello World!"
)

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

val MyTypography = Typography(
    labelMedium = TextStyle(
        fontFamily = fontFamily, fontWeight = FontWeight.Normal, fontSize = 12.sp/*...*/
    ),
    labelLarge = TextStyle(
        fontFamily = fontFamily,
        fontWeight = FontWeight.Bold,
        letterSpacing = 2.sp,
        /*...*/
    ),
    displayMedium = TextStyle(
        fontFamily = fontFamily, fontWeight = FontWeight.SemiBold/*...*/
    ),
    /*...*/
)

بعد ذلك، اضبط أسلوب الخط على موضوع تطبيقك:

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 على الجهاز.

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

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

ابدأ بإنشاء 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 التي تعرض القيمة false إذا تم إعداد موفِّر الخدمة بشكل غير صحيح.

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 ضبط محاور الخط العادية، مثل weight وwidth وSlant. هذه محاور قياسية متوفرة بأي خط متغير. يمكنك إنشاء تكوينات مختلفة للخط بناءً على المكان الذي سيتم استخدام الخط فيه.

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

    // 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. اضبط أسلوب الخط في Material Design 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. الآن بعد أن حددت أسلوب الخط، عليك تمريره إلى MaterialTheme M3:

    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 لاسترداد أسلوب الخط المقدَّم في العنصر القابل للإنشاء M3 MaterialTheme.

ثلاثة نصوص مختلفة، تعرض جميعها إعدادات خطوط مختلفة.
الشكل 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. نص يعرض محاور مخصّصة تم ضبطها على خطوط متغيّرة.

مصادر إضافية

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