การทำงานกับแบบอักษร

หน้านี้จะอธิบายวิธีตั้งค่าแบบอักษรในแอป Compose

ตั้งค่าแบบอักษร

Text มีพารามิเตอร์ fontFamily เพื่ออนุญาตให้ตั้งค่าแบบอักษรที่ใช้ใน Composable โดยค่าเริ่มต้น ระบบจะรวมชุดแบบอักษร 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 นี้ไปยัง Composable 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 เป็นต้นไป คุณสามารถใช้ API แบบอักษรที่ดาวน์โหลดได้ในแอป Compose เพื่อดาวน์โหลด Google Fonts แบบไม่พร้อมกันและใช้ในแอปได้

ขณะนี้ระบบยังไม่รองรับแบบอักษรที่ดาวน์โหลดได้ซึ่งมาจากผู้ให้บริการที่กำหนดเอง

ใช้แบบอักษรที่ดาวน์โหลดได้แบบเป็นโปรแกรม

หากต้องการดาวน์โหลดแบบอักษรแบบเป็นโปรแกรมจากภายในแอป ให้ทำตามขั้นตอนต่อไปนี้

  1. เพิ่มทรัพยากร Dependency ดังนี้

    ดึงดูด

    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(
    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)
)

คุณสามารถกำหนดค่าเริ่มต้นสำหรับแบบอักษรสำหรับทั้ง 2 น้ำหนักได้ดังนี้

// ...
 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 ที่มี 2 เชน โดยมี 1 เชนต่อน้ำหนัก กลไกการโหลดจะพยายามแก้ไขแบบอักษรออนไลน์ก่อน จากนั้นจึงแก้ไขแบบอักษรที่อยู่ในโฟลเดอร์ทรัพยากร R.font ในเครื่อง

แก้ไขข้อบกพร่องในการติดตั้งใช้งาน

คุณสามารถกำหนดตัวแฮนเดิล Coroutine สำหรับการแก้ไขข้อบกพร่องเพื่อช่วยยืนยันว่ามีการดาวน์โหลดแบบอักษรอย่างถูกต้องหรือไม่ แฮนเดิลจะระบุลักษณะการทำงานของสิ่งที่ต้องทำในกรณีที่โหลดแบบอักษรแบบอะซิงโครนัสไม่สำเร็จ

เริ่มต้นด้วยการสร้าง CoroutineExceptionHandler ดังนี้

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

ส่งไปยังเมธอด createFontFamilyResolver เพื่อให้ Resolver ใช้แฮนเดิลใหม่

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

นอกจากนี้ คุณยังใช้ API isAvailableOnDevice จากผู้ให้บริการเพื่อทดสอบว่าผู้ให้บริการพร้อมใช้งานและกำหนดค่าใบรับรองอย่างถูกต้องได้ด้วย โดยคุณสามารถเรียกใช้เมธอด isAvailableOnDevice ซึ่งจะแสดงผลเป็นเท็จหากกำหนดค่าผู้ให้บริการไม่ถูกต้อง

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

ข้อควรระวัง

Google Fonts ใช้เวลาหลายเดือนในการทำให้แบบอักษรใหม่พร้อมใช้งานใน Android จะมีช่วงเวลาระหว่างที่มีการเพิ่มแบบอักษรใน fonts.google.com และเมื่อแบบอักษรพร้อมใช้งานผ่าน API แบบอักษรที่ดาวน์โหลดได้ (ไม่ว่าจะในระบบ View หรือใน Compose) แบบอักษรที่เพิ่มใหม่อาจโหลดในแอปไม่สำเร็จโดยมีข้อผิดพลาด IllegalStateException เราได้เพิ่มข้อความอธิบายสำหรับข้อยกเว้นใน Compose พร้อมการเปลี่ยนแปลงที่นี่เพื่อช่วยให้นักพัฒนาแอปสามารถระบุข้อผิดพลาดนี้ได้เหนือกว่าข้อผิดพลาดในการโหลดแบบอักษรประเภทอื่นๆ หากพบปัญหา โปรดรายงานปัญหาโดยใช้เครื่องมือติดตามปัญหา

ใช้แบบอักษรที่ปรับแต่งได้

แบบอักษร Variable เป็นรูปแบบของแบบอักษรที่ช่วยให้ไฟล์แบบอักษร 1 ไฟล์มีรูปแบบที่แตกต่างกันได้ แบบอักษรที่ปรับขนาดได้ช่วยให้คุณแก้ไขแกน (หรือพารามิเตอร์) เพื่อสร้างสไตล์ที่ต้องการได้ แกนเหล่านี้อาจเป็นแกนมาตรฐาน เช่น น้ำหนัก ความกว้าง ความเอียง และตัวเอียง หรือแกนที่กำหนดเอง ซึ่งจะแตกต่างกันไปตามแบบอักษรที่ปรับขนาดได้

การกำหนดค่า 5 แบบของแบบอักษรตัวแปรเดียวกันที่มีค่าแกนต่างกัน
รูปที่ 1 ข้อความที่ใช้แบบอักษรตัวแปรเดียวกันซึ่งปรับแต่งด้วยค่าแกนที่ต่างกัน

การใช้แบบอักษรที่ปรับขนาดได้แทนไฟล์แบบอักษรปกติจะช่วยให้คุณมีไฟล์แบบอักษรเพียงไฟล์เดียวแทนที่จะมีหลายไฟล์

ดูข้อมูลเพิ่มเติมเกี่ยวกับแบบอักษรที่เปลี่ยนแปลงได้ที่ความรู้เกี่ยวกับ Google Fonts, แคตตาล็อกทั้งหมดของแบบอักษรที่เปลี่ยนแปลงได้ที่พร้อมใช้งาน และตารางแกนที่รองรับสำหรับแบบอักษรแต่ละแบบ

เอกสารนี้แสดงวิธีใช้แบบอักษรที่ปรับขนาดได้ในแอป Compose

โหลดแบบอักษรที่ปรับแต่งได้

  1. ดาวน์โหลดแบบอักษร Variable ที่ต้องการใช้ (เช่น 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 ช่วยให้คุณกำหนดค่าแกนแบบอักษรมาตรฐานได้ เช่น น้ำหนัก ความกว้าง และความเอียง ซึ่งเป็นแกนมาตรฐานที่ใช้ได้กับแบบอักษร Variable Font ทุกแบบ คุณสามารถสร้างการกำหนดค่าแบบอักษรที่แตกต่างกันได้ตามตำแหน่งที่จะใช้แบบอักษร

  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. เมื่อกำหนดรูปแบบตัวพิมพ์แล้ว ให้ส่งไปยัง MaterialTheme ของ M3 ดังนี้

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

  7. สุดท้าย ให้ใช้ Text Composable และระบุรูปแบบเป็นรูปแบบตัวพิมพ์ที่กำหนดไว้รูปแบบใดรูปแบบหนึ่ง 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 เพื่อดึงข้อมูลการจัดพิมพ์ที่ระบุไว้ใน Composable MaterialTheme ของ M3

ข้อความ 3 แบบที่แตกต่างกัน ซึ่งทั้งหมดแสดงการกำหนดค่าแบบอักษรที่แตกต่างกัน
รูปที่ 2 แบบอักษร Variable ที่ใช้ในการกำหนดค่าที่แตกต่างกัน 3 แบบ

ใช้แกนที่กำหนดเอง

นอกจากนี้ แบบอักษรยังมีแกนที่กำหนดเองได้ด้วย ซึ่งกำหนดไว้ภายในไฟล์แบบอักษรเอง ตัวอย่างเช่น แบบอักษร 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 แบบที่แสดงการกำหนดค่าต่างๆ สำหรับแบบอักษรที่ปรับขนาดได้ โดยมีการตั้งค่าแกนที่กำหนดเอง บางแบบมีตัวอักษรพิมพ์เล็กที่สูงขึ้นและกว้างกว่าเดิม
รูปที่ 3 ข้อความแสดงแกนที่กำหนดเองซึ่งตั้งค่าไว้ในแบบอักษรที่ปรับขนาดได้

แหล่งข้อมูลเพิ่มเติม

ดูข้อมูลเพิ่มเติมได้ที่บล็อกโพสต์เกี่ยวกับแบบอักษรที่ปรับเปลี่ยนได้ต่อไปนี้