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

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

تنظیم قلم

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

@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 composable خود منتقل کنید. چون 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 نسخه ۱.۲.۰، می‌توانید از 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 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/*...*/
    ),
    /*...*/
)

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

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

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

هشدارها

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

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

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

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

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

برای اطلاعات بیشتر درباره قلم‌های متغیر، به دانش 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 API به شما امکان می‌دهد محورهای قلم استاندارد مانند وزن، عرض، و شیب را پیکربندی کنید. این‌ها محورهای استانداردی هستند که با هر قلم متغیری دردسترس‌اند. می‌توانید پیکربندی‌های مختلفی از قلم را براساس جایی که قلم استفاده خواهد شد ایجاد کنید.

  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 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. اکنون که تایپوگرافی خود را تعریف کرده‌اید، آن را به 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
    }

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

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

منابع بیشتر

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