คำอธิบายประกอบ @Composable เป็นองค์ประกอบพื้นฐานของ Jetpack Compose ที่เปลี่ยนฟังก์ชัน Kotlin ทั่วไปให้เป็นบล็อกการสร้างส่วนติดต่อผู้ใช้แบบประกาศ หากไม่มีคำอธิบายประกอบนี้ จะไม่สามารถสร้างหน้าจอใดๆ ในการพัฒนา Android สมัยใหม่ได้ ตามข้อมูลของ Google Android Developers, 2026 กว่า 80% ของโปรเจกต์ Kotlin ใหม่ใช้ Compose ในการสร้าง UI และ @Composable เป็นคำอธิบายประกอบที่ใช้บ่อยที่สุดในระบบนิเวศ
ประเด็นสำคัญ
@Composable คือคำอธิบายประกอบของภาษา Kotlin ที่ทำเครื่องหมายฟังก์ชันว่ามีไว้สำหรับอธิบายส่วนติดต่อผู้ใช้ในเฟรมเวิร์ก Jetpack Compose เมื่อคอมไพเลอร์ Kotlin พบคำอธิบายประกอบนี้ มันจะสร้างโค้ดเพิ่มเติมที่ช่วยให้ฟังก์ชันทำงานในบริบทของการประกอบ — ระบบจัดการโครงสร้าง UI
คำอธิบายประกอบ @Composable ถูกนำเสนอโดย Google ในปี 2021 พร้อมกับเวอร์ชันเสถียรแรกของ Jetpack Compose 1.0 ก่อนที่จะปรากฏ การพัฒนาส่วนติดต่อ Android ดำเนินการผ่าน XML มาร์กอัปและระบบ View เท่านั้น @Composable เปลี่ยนแนวทางอย่างสิ้นเชิง: แทนที่จะอธิบาย UI ในไฟล์มาร์กอัปแยกต่างหาก นักพัฒนาจะเขียนส่วนติดต่อโดยตรงใน Kotlin
ความแตกต่างหลักระหว่าง @Composable และฟังก์ชัน Kotlin ทั่วไปคือความสามารถในการอ่านและตอบสนองต่อการเปลี่ยนแปลงสถานะ เมื่อตัวแปรที่ฟังก์ชัน Composable อ่านเปลี่ยนค่า ระบบจะกำหนดเวลาการเริ่มต้นใหม่ (การประกอบใหม่) ของฟังก์ชันนั้นโดยอัตโนมัติ ซึ่งช่วยให้นักพัฒนาไม่ต้องอัปเดต UI ด้วยตนเองผ่าน findViewById และ setText
กลไกภายในของ @Composable อาศัยแนวคิดของสล็อต — พื้นที่หน่วยความจำพิเศษที่จัดสรรให้กับแต่ละฟังก์ชันภายในองค์ประกอบ สล็อตนี้เก็บค่าที่ส่งไปยังฟังก์ชัน รวมถึงข้อมูลบริการที่จำเป็นสำหรับการเปรียบเทียบในการเรียกครั้งต่อไป
ในการประกาศฟังก์ชัน Composable เพียงเพิ่มคำอธิบายประกอบ @Composable หน้าคีย์เวิร์ด fun ฟังก์ชันต้องอยู่ในแพ็กเกจที่นำเข้าคำอธิบายประกอบจาก androidx.compose.runtime แนะนำให้เขียนชื่อฟังก์ชันด้วยตัวพิมพ์ใหญ่ — นี่คือธรรมเนียมที่ยอมรับอย่างกว้างขวางในชุมชน Compose ซึ่งแยกความแตกต่างระหว่างคอมโพเนนต์ UI กับฟังก์ชันทั่วไปด้วยสายตา
import androidx.compose.runtime.Composable
@Composable
fun Greeting(name: String) {
var count by remember { mutableStateOf(0) }
Column {
Text("สวัสดี, $name!")
Button(onClick = { count++ }) {
Text("คลิก $count ครั้ง")
}
}
}
พารามิเตอร์ของฟังก์ชัน Composable สามารถเป็นอะไรก็ได้ — ชนิดดั้งเดิม สตริง แลมบ์ดา และแม้แต่ฟังก์ชัน Composable อื่นๆ ที่ส่งผ่าน Slot API แนะนำ ให้ทำให้พารามิเตอร์ไม่เปลี่ยนรูป (val) เพื่อหลีกเลี่ยงผลข้างเคียงระหว่างการประกอบใหม่ ข้อมูลที่เปลี่ยนแปลงได้ทั้งหมดควรจัดการผ่านกลไกสถานะของ Compose
ฟังก์ชัน Composable ไม่สามารถคืนค่าใดๆ ได้ตามอำเภอใจเหมือนฟังก์ชันทั่วไป — งานเดียวของพวกเขาคือสร้างหรืออัปเดตส่วนหนึ่งของโครงสร้าง UI อย่างไรก็ตาม มีรูปแบบพิเศษเช่น State Hoisting ซึ่งฟังก์ชัน Compose รับสถานะและ callback ผ่านพารามิเตอร์ โดยคงความบริสุทธิ์และสามารถนำกลับมาใช้ใหม่ได้
ระบบ Compose กำหนดข้อจำกัดที่เข้มงวดหลายประการเกี่ยวกับลักษณะและพฤติกรรมของ ฟังก์ชัน Composable กฎข้อแรก: ฟังก์ชัน Composable สามารถเรียกใช้เฉพาะฟังก์ชัน Composable อื่นๆ หรือฟังก์ชันทั่วไปที่ไม่มีผลข้างเคียงเท่านั้น ซึ่งรับประกันความสามารถในการคาดเดาของการประกอบและการทำงานที่ถูกต้องของการปรับแต่งของ Compose
กฎข้อที่สองเกี่ยวกับลำดับการทำงาน Compose มีสิทธิ์เรียกใช้ฟังก์ชัน Composable ใน ลำดับใดก็ได้ ดังนั้นโค้ดในเนื้อหาของฟังก์ชันดังกล่าวต้องไม่ขึ้นอยู่กับลำดับการเรียกของฟังก์ชันข้างเคียง แต่ละฟังก์ชัน Composable ต้องพึ่งพาตนเองได้ในระดับตำแหน่งของมันในโครงสร้าง UI
กฎข้อที่สาม — ห้ามผลข้างเคียง ภายในเนื้อหาของฟังก์ชัน Composable การดำเนินการเช่นการเขียนฐานข้อมูล การส่งคำขอเครือข่าย หรือการแก้ไขตัวแปรภายนอกต้องทำภายในเอฟเฟกต์พิเศษเท่านั้น: LaunchedEffect, DisposableEffect หรือ SideEffect การละเมิดกฎนี้จะนำไปสู่พฤติกรรมที่คาดเดาไม่ได้ระหว่างการประกอบใหม่
กฎข้อที่สี่: ฟังก์ชัน Composable ต้องมีคุณสมบัติ idempotent การเรียกซ้ำด้วยอาร์กิวเมนต์เดียวกันควรสร้าง UI เดียวกัน ข้อกำหนดนี้จำเป็นสำหรับการทำงานที่ถูกต้องของการปรับแต่งการข้าม ซึ่ง Compose จะข้ามการวาดซ้ำของฟังก์ชันที่ข้อมูลอินพุตไม่เปลี่ยนแปลง
// ถูกต้อง: ฟังก์ชัน Composable บริสุทธิ์ไม่มีผลข้างเคียง
@Composable
fun UserCard(user: User, onClick: () -> Unit) {
Card(modifier = Modifier.clickable { onClick() }) {
Text(text = user.name)
}
}
// ผิด: ผลข้างเคียงภายในเนื้อหา
@Composable
fun WrongCard(userId: String) {
// val result = viewModel.loadUser(userId) // ไม่ได้รับอนุญาต
Text("กำลังโฬหด...")
}
มาดูตัวอย่างการใช้งานจริงของการสร้างหน้าจอโปรไฟล์โดยใช้คำอธิบายประกอบ @Composable ที่นี่เราสาธิตการรวมหลายฟังก์ชัน Composable การทำงานกับสถานะและตัวปรับแต่ง — องค์ประกอบสำคัญของเลย์เอาต์ Compose ใดๆ
@Composable
fun ProfileScreen(userId: String) {
var isFollowed by remember { mutableStateOf(false) }
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
ProfileHeader(userId = userId)
Spacer(modifier = Modifier.height(16.dp))
StatsRow(posts = 42, followers = 1280)
Spacer(modifier = Modifier.height(24.dp))
FollowButton(
isFollowed = isFollowed,
onToggle = { isFollowed = !isFollowed }
)
}
}
@Composable
fun ProfileHeader(userId: String) {
Row(verticalAlignment = Alignment.CenterVertically) {
AsyncImage(model = "https://example.com/avatars/$userId",
contentDescription = "User avatar")
Spacer(modifier = Modifier.width(12.dp))
Text(text = "ผู้ใช้ #$userId", style = MaterialTheme.typography.headlineMedium)
}
}
@Composable
fun StatsRow(posts: Int, followers: Int) {
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) {
StatItem("Posts", posts)
StatItem("Followers", followers)
}
}
@Composable
fun StatItem(label: String, value: Int) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "$value", style = MaterialTheme.typography.headlineSmall)
Text(text = label, style = MaterialTheme.typography.bodySmall)
}
}
ในตัวอย่าง แต่ละฟังก์ชัน Composable รับผิดชอบส่วนของหน้าจอตัวเอง: ProfileScreen จัดการสถานะโดยรวมและการประกอบของฟังก์ชันลูก ProfileHeader แสดงอวาตาร์และชื่อ และ StatsRow แสดงบล็อกสถิติ แนวทางนี้เป็นไปตามหลักการความรับผิดชอบเดียวและทำให้การใช้คอมโพเนนต์ซ้ำง่ายขึ้น
ใน Jetpack Compose มี ฟังก์ชัน Composable หลักสามประเภท ประเภทแรก — คอนเทนเนอร์ (Row, Column, Box, LazyColumn) — กำหนดการจัดวางขององค์ประกอบลูก ประเภทที่สอง — องค์ประกอบการแสดงผล (Text, Image, Icon, Button) — เรนเดอร์คอมโพเนนต์ UI เฉพาะ ประเภทที่สาม — ฟังก์ชัน Composable ที่กำหนดเอง — รวมคอมโพเนนต์ในตัวเป็นบล็อกที่ใช้ซ้ำได้
คอนเทนเนอร์แตกต่างจากองค์ประกอบทั่วไปตรงที่รับ lambda content — พารามิเตอร์สุดท้ายชนิด @Composable () -> Unit กลไกนี้ช่วยให้สร้างโครงสร้าง UI ที่ซ้อนกันได้: แต่ละคอนเทนเนอร์สร้างการประกอบลูกที่มีบริบทและพื้นที่หน่วยความจำของตัวเอง
ฟังก์ชัน Composable ที่กำหนดเองแบ่งออกเป็นสองประเภทย่อย: สมาร์ท (smart) และ ธรรมดา (dumb) ฟังก์ชันสมาร์ทจัดการสถานะและตรรกะ — ประกอบด้วยการเรียก remember, LaunchedEffect และ API อื่นๆ ของ Compose ฟังก์ชันธรรมดารับข้อมูลทั้งหมดผ่านพารามิเตอร์และแสดงผลเท่านั้น การแยกคอมโพเนนต์สมาร์ทและธรรมดาช่วยปรับปรุงความสามารถในการทดสอบและการใช้โค้ดซ้ำ
| ประเภท | ตัวอย่าง | วัตถุประสงค์ |
|---|---|---|
| คอนเทนเนอร์ | Column, Row, Box | จัดการการจัดวางองค์ประกอบลูก |
| องค์ประกอบ | Text, Image, Button | แสดงเนื้อหาและจัดการอินพุต |
| กำหนดเอง | ProfileCard, UserList | รวมคอมโพเนนต์มาตรฐาน |
ข้อได้เปรียบหลักของคำอธิบายประกอบ @Composable คือความสามารถในการสร้างคอมโพเนนต์ UI ที่ใช้ซ้ำได้โดยไม่ต้องมีการสืบทอดและลำดับชั้นคลาสที่ซับซ้อน ต่างจากระบบ View ที่แต่ละองค์ประกอบที่กำหนดเองต้องสร้างคลาส Java พร้อมคอนสตรัคเตอร์ คอมโพเนนต์ Composable เป็นเพียงฟังก์ชัน Kotlin ที่มีพารามิเตอร์
เพื่อรับประกันการใช้ซ้ำ จะใช้รูปแบบ Slot API ซึ่งฟังก์ชัน Composable รับ lambda content สำหรับพื้นที่ต่างๆ ของเลย์เอาต์ ตัวอย่างเช่น คอมโพเนนต์ Card สามารถรับเนื้อหาแยกต่างหากสำหรับส่วนหัว ส่วนเนื้อหา และส่วนท้าย ทำให้มันใช้งานได้ทั่วไปกับทุกหน้าจอของแอปพลิเคชัน
ตัวปรับแต่ง (Modifier) มีบทบาทสำคัญในการใช้ซ้ำ: อนุญาตให้กำหนดค่าระยะห่าง ขนาด การคลิก และแอนิเมชันโดยไม่ต้องเปลี่ยนคอมโพเนนต์本身 ขอแนะนำให้ส่ง Modifier เป็นพารามิเตอร์ของฟังก์ชัน Composable ด้วยค่าเริ่มต้นเสมอ: Modifier = Modifier — นี่คือแนวทางปฏิบัติมาตรฐานที่นำมาใช้ในไลบรารีทางการของ Google
@Composable
fun SectionCard(
modifier: Modifier = Modifier,
title: String,
content: @Composable () -> Unit
) {
Card(modifier = modifier) {
Column(modifier = Modifier.padding(16.dp)) {
Text(text = title, style = MaterialTheme.typography.titleMedium)
Spacer(modifier = Modifier.height(8.dp))
content()
}
}
}
ด้วย Slot API คอมโพเนนต์ SectionCard สามารถใช้บนหน้าจอต่างๆ ที่มีเนื้อหาต่างกัน — ฟอร์ม รายการ บล็อกข้อความ การรวมกัน ของตัวปรับแต่งและ Slot API ทำให้คอมโพเนนต์ Compose มีความยืดหยุ่นสูงโดยไม่สูญเสียความปลอดภัยของชนิดที่ Kotlin มอบให้
คำถามที่พบบ่อย
ฟังก์ชัน @Composable ทำงานในบริบทของการประกอบและสามารถอ่านสถานะ โดยเริ่มต้นใหม่โดยอัตโนมัติเมื่อมีการเปลี่ยนแปลง ฟังก์ชัน Kotlin ทั่วไปไม่มีสิทธิ์เข้าถึงกลไกการติดตามสถานะและไม่มีส่วนร่วมในการสร้างโครงสร้าง UI
ไม่ ฟังก์ชัน Composable สามารถเรียกได้จากฟังก์ชัน Composable อื่นเท่านั้น เนื่องจากต้องใช้บริบทการประกอบพิเศษ ในการรวมโค้ด Compose กับ Kotlin ปกติ ให้ใช้เมธอด setContent { } ใน Activity หรือ ComposeView ในระบบ View
นี่คือธรรมเนียมการตั้งชื่อที่นำมาใช้ในชุมชน Compose ตัวพิมพ์ใหญ่แยกความแตกต่างระหว่าง คอมโพเนนต์ UI กับฟังก์ชันทั่วไปด้วยสายตา ตามกฎการตั้งชื่อคลาส ไม่ใช่ข้อกำหนดของคอมไพเลอร์ แต่เป็นแนวทางปฏิบัติที่แนะนำในเอกสารของ Google
ไม่มีข้อจำกัดเกี่ยวกับจำนวน ในทางปฏิบัติ หน้าจอขนาดใหญ่สามารถมี 50–100 ฟังก์ชัน Composable รวมถึงคอมโพเนนต์ในตัว (Text, Button) และที่กำหนดเอง Compose ปรับแต่งโครงสร้างฟังก์ชันและดำเนินการเฉพาะฟังก์ชันที่ข้อมูลอินพุตเปลี่ยนแปลง
โดยปกติฟังก์ชัน Composable คืนค่า Unit เนื่องจากงานของพวกเขาคือสร้าง UI อย่างไรก็ตาม มีฟังก์ชัน เฉพาะทาง เช่น remember และ derivedStateOf ที่ถูกทำเครื่องหมายด้วย @Composable และคืนค่า นี่เป็นข้อยกเว้น ไม่ใช่กฎ
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม