کار کردن با قلم‌ها

این صفحه نحوه تنظیم قلم‌ها در برنامه Compose را شرح می‌دهد.

تنظیم قلم

‫Text پارامتر fontFamily را دارد که امکان تنظیم قلم استفاده‌شده در ترکیب‌پذیر را فراهم می‌کند. به‌طور پیش‌فرض، خانواده‌های قلم سریف، سن‌سریف، تک‌فاصله، و دست‌نویس گنجانده شده‌اند:

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

کلمات

می‌توانید از مشخصه fontFamily برای کار با قلم‌ها و طرح حروف سفارشی تعریف‌شده در پوشه res/font استفاده کنید:

نمایش گرافیکی پوشه res > font در محیط توسعه

این مثال نشان می‌دهد که چگونه براساس آن فایل‌های قلم و بااستفاده از تابع Font، یک fontFamily را تعریف می‌کنید:

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 خود ارسال کنید. چون a 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 شروع می‌شود، می‌توانید از API قلم‌های بارگیری‌شدنی در برنامه Compose خود برای بارگیری ناهم‌زمان قلم‌های Google و استفاده از آن‌ها در برنامه‌تان استفاده کنید.

درحال‌حاضر، از قلم‌های قابل‌بارگیری ارائه‌شده توسط ارائه‌دهندگان سفارشی پشتیبانی نمی‌شود.

استفاده از قلم‌های قابل‌بارگیری به‌صورت برنامه‌ریزی‌شده

برای بارگیری برنامه‌ای قلم به‌صورت برنامه‌ریزی‌شده از داخل برنامه، این مراحل را دنبال کنید:

  1. وابستگی را اضافه کنید:

    شیک

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

    کاتلین

    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».
    • بسته ارائه‌دهنده قلم برای درستی‌سنجی هویت ارائه‌دهنده.
    • فهرستی از مجموعه‌های درهم‌سازی برای گواهینامه‌ها به‌منظور درستی‌سنجی هویت ارائه‌دهنده. می‌توانید در برنامه نمونه Jetchat در فایل font_certs.xml مقادیر درهم‌سازی موردنیاز برای ارائه‌دهنده Google Fonts را پیدا کنید.
  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(
    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/*...*/
    ),
    /*...*/
)

سپس، «حروف‌چینی» را روی زمینه برنامه‌تان تنظیم کنید:

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

برای نمونه‌ای از برنامه‌ای که در «نوشتن» فونت‌های بارگیری‌شدنی را همراه با 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 API ارائه‌دهنده برای آزمایش اینکه آیا ارائه‌دهنده دردسترس است و گواهینامه‌ها به‌درستی پیکربندی شده‌اند استفاده کنید. برای انجام این کار، می‌توانید روش isAvailableOnDevice را فراخوانی کنید که اگر ارائه‌دهنده به‌درستی پیکربندی نشده باشد، مقدار نادرست را برمی‌گرداند.

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

ملاحظات

چند ماه طول می‌کشد تا «قلم‌های Google» قلم‌های جدید را در Android دردسترس قرار دهد. بین زمانی که قلمی در fonts.google.com اضافه می‌شود و زمانی که ازطریق میانای برنامه‌سازی کاربردی قلم‌های قابل‌بارگیری (چه در سیستم «مشاهده» و چه در «نوشتن») دردسترس قرار می‌گیرد فاصله زمانی وجود دارد. قلم‌های جدیداً اضافه‌شده ممکن است در برنامه شما با IllegalStateException بار نشوند. برای کمک به توسعه‌دهندگان در شناسایی این خطا در میان انواع دیگر خطاهای بار کردن قلم، پیام‌های توصیفی برای استثنا در «ترکیب کردن» با تغییرات در اینجا اضافه کردیم. اگر مشکلی پیدا کردید، آن را بااستفاده از ردیاب مشکل گزارش کنید.

استفاده از قلم‌های متغیر

قلم متغیر قالب قلمی است که به یک فایل قلم اجازه می‌دهد سبک‌های مختلفی داشته باشد. با قلم‌های متغیر، می‌توانید محورها (یا پارامترها) را تغییر دهید تا سبک موردنظرتان را تولید کنید. این محورها می‌توانند استاندارد باشند، مانند وزن، عرض، کجی، و ایتالیک، یا سفارشی، که در قلم‌های متغیر متفاوت هستند.

پنج پیکربندی از همان قلم متغیر با مقادیر محور متفاوت.
شکل ۱. نوشتار بااستفاده از همان قلم متغیر که با مقادیر محور متفاوت سفارشی‌سازی شده است.

استفاده از قلم‌های متغیر به‌جای فایل‌های قلم معمولی به شما امکان می‌دهد به‌جای چند فایل قلم، فقط یک فایل قلم داشته باشید.

برای اطلاعات بیشتر درباره قلم‌های متغیر، به دانش Google Fonts، کاتالوگ کامل قلم‌های متغیر دردسترس، و جدول محورهای پشتیبانی‌شده برای هر قلم مراجعه کنید.

این سند نحوه پیاده‌سازی قلم متغیر در برنامه 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. حروف‌چینی «طراحی Material 3» را پیکربندی کنید تا از FontFamily استفاده کند:

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

    این نمونه از displayLarge سبک حروف‌چینی Material 3 استفاده می‌کند که تنظیمات پیش‌فرض قلم و کاربردهای توصیه‌شده متفاوتی دارد. برای مثال، باید از displayLarge برای نوشتار کوتاه و مهم استفاده کنید، زیرا این نوشتار بزرگ‌ترین نوشتار روی صفحه است.

    با «طراحی مادی ۳»، می‌توانید مقادیر پیش‌فرض 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 قابل ترکیب ازطریق سبک «تم Material» آن پیکربندی می‌شود و پیکربندی قلم متغیر متفاوتی دارد. می‌توانید از MaterialTheme.typography برای بازیابی حروف‌چینی ارائه‌شده به MaterialTheme عنصر ترکیبی M3 استفاده کنید.

سه نوشتار مختلف که هرکدام پیکربندی‌های مختلف قلم را نمایش می‌دهند.
شکل ۲. قلم متغیر در سه پیکربندی مختلف اعمال شد.

استفاده از محورهای سفارشی

قلم‌ها همچنین می‌توانند محورهای سفارشی داشته باشند. این‌ها در خود فایل قلم تعریف شده‌اند. برای مثال، قلم 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
    }

    توجه کنید که ارتفاع حروف بالابلند کوچک اکنون افزایش یافته است و نوشتار دیگر پهن‌تر است:

سه نوشتار مختلف که پیکربندی‌های متفاوتی را برای قلم‌های متغیر نشان می‌دهد، با محورهای سفارشی تنظیم‌شده— برخی‌از آن‌ها دارای بالابر حروف کوچک بلندتر هستند و پهن‌تر از قبل هستند.
شکل ۳. نوشتاری که محورهای سفارشی تنظیم‌شده در قلم‌های متغیر را نشان می‌دهد.

منابع بیشتر

برای اطلاعات بیشتر، پست وبلاگ زیر را درباره قلم‌های متغیر ببینید: