ใน Compose UI จะเปลี่ยนแปลงไม่ได้ ซึ่งหมายความว่าคุณจะอัปเดต UI หลังจากวาดแล้วไม่ได้
สิ่งที่คุณควบคุมได้คือสถานะของ UI ทุกครั้งที่สถานะของ
UI เปลี่ยนไป Compose จะสร้างส่วนของแผนผัง UI ที่มีการเปลี่ยนแปลงขึ้นมาใหม่ Composable สามารถรับสถานะและแสดงเหตุการณ์ได้ เช่น TextField รับค่าและแสดงการเรียกกลับ onValueChange ที่
ขอให้ตัวแฮนเดิลการเรียกกลับเปลี่ยนค่า
var name by remember { mutableStateOf("") } OutlinedTextField( value = name, onValueChange = { name = it }, label = { Text("Name") } )
เนื่องจาก Composable ยอมรับสถานะและแสดงเหตุการณ์ รูปแบบการไหลของข้อมูลแบบทางเดียว จึงเหมาะกับ Jetpack Compose คู่มือนี้มุ่งเน้นวิธีใช้รูปแบบโฟลว์ข้อมูลแบบทิศทางเดียวใน Compose, วิธีใช้ตัวเก็บสถานะและเหตุการณ์ และวิธีใช้ ViewModel ใน Compose
การไหลของข้อมูลแบบทางเดียว
การไหลของข้อมูลแบบทิศทางเดียว (UDF) คือรูปแบบการออกแบบที่สถานะไหลลง และเหตุการณ์ไหลขึ้น การใช้โฟลว์ข้อมูลแบบทิศทางเดียวจะช่วยให้คุณแยกส่วนประกอบที่แสดงสถานะใน UI ออกจากส่วนต่างๆ ของแอปที่จัดเก็บและเปลี่ยนสถานะได้
วงจรการอัปเดต UI สำหรับแอปที่ใช้การไหลของข้อมูลแบบทิศทางเดียวจะมีลักษณะดังนี้
- เหตุการณ์: ส่วนหนึ่งของ UI สร้างเหตุการณ์และส่งต่อขึ้นไป เช่น การคลิกปุ่มที่ส่งไปยัง ViewModel เพื่อจัดการ หรือเหตุการณ์ที่ส่งจาก เลเยอร์อื่นๆ ของแอป เช่น การระบุว่าเซสชันของผู้ใช้หมดอายุแล้ว
- อัปเดตสถานะ: ตัวแฮนเดิลเหตุการณ์อาจเปลี่ยนสถานะ
- สถานะการแสดงผล: ผู้ถือสถานะจะส่งต่อสถานะ และ UI จะแสดงสถานะนั้น
การทำตามรูปแบบนี้เมื่อใช้ Jetpack Compose มีข้อดีหลายประการ ดังนี้
- ความสามารถในการทดสอบ: การแยกสถานะออกจาก UI ที่แสดงสถานะจะช่วยให้ทดสอบทั้ง 2 อย่างแยกกันได้ง่ายขึ้น
- การห่อหุ้มสถานะ: เนื่องจากอัปเดตสถานะได้ในที่เดียวเท่านั้นและมีแหล่งข้อมูลความจริงเพียงแหล่งเดียวสำหรับสถานะของ Composable จึงมีโอกาสน้อยที่จะสร้างข้อบกพร่องเนื่องจากสถานะไม่สอดคล้องกัน
- ความสอดคล้องของ UI: การอัปเดตสถานะทั้งหมดจะแสดงใน UI ทันทีโดย
การใช้ตัวยึดสถานะที่สังเกตได้ เช่น
StateFlowหรือLiveData
การไหลของข้อมูลแบบทิศทางเดียวใน Jetpack Compose
Composable ทำงานโดยอิงตามสถานะและเหตุการณ์ ตัวอย่างเช่น TextField จะอัปเดตก็ต่อเมื่อมีการอัปเดตพารามิเตอร์ value และแสดงการเรียกกลับ onValueChange
ซึ่งเป็นเหตุการณ์ที่ขอให้เปลี่ยนค่าเป็นค่าใหม่ Compose
กำหนดStateออบเจ็กต์เป็นตัวยึดค่า และการเปลี่ยนแปลงค่าสถานะ
จะทริกเกอร์การจัดองค์ประกอบใหม่ คุณสามารถเก็บสถานะไว้ใน
remember { mutableStateOf(value) } หรือ
rememberSaveable { mutableStateOf(value) ได้โดยขึ้นอยู่กับระยะเวลาที่ต้องการ
จดจำค่า
ประเภทของค่าของ TextField composable คือ String ดังนั้นค่านี้จึงมาจากที่ใดก็ได้ ไม่ว่าจะเป็นค่าที่ฮาร์ดโค้ด ค่าจาก ViewModel หรือค่าที่ส่งมาจาก
composable หลัก คุณไม่จำเป็นต้องเก็บไว้ในออบเจ็กต์ State แต่ต้องอัปเดตค่าเมื่อมีการเรียกใช้ onValueChange
กำหนดพารามิเตอร์ที่ใช้ร่วมกันได้
เมื่อกำหนดพารามิเตอร์สถานะของ Composable โปรดคำนึงถึงคำถามต่อไปนี้
- Composable นำกลับมาใช้ใหม่หรือมีความยืดหยุ่นเพียงใด
- พารามิเตอร์สถานะส่งผลต่อประสิทธิภาพของ Composable นี้อย่างไร
Composable แต่ละรายการควรมีข้อมูลน้อยที่สุดเท่าที่จะเป็นไปได้เพื่อส่งเสริมการแยกส่วนและการนำกลับมาใช้ใหม่ ตัวอย่างเช่น เมื่อสร้าง Composable เพื่อเก็บส่วนหัวของบทความข่าว ให้ส่งเฉพาะข้อมูลที่ต้องแสดงแทนที่จะส่งบทความข่าวทั้งหมด
@Composable fun Header(title: String, subtitle: String) { // Recomposes when title or subtitle have changed. } @Composable fun Header(news: News) { // Recomposes when a new instance of News is passed in. }
บางครั้งการใช้พารามิเตอร์แต่ละรายการยังช่วยปรับปรุงประสิทธิภาพได้ด้วย เช่น หาก
News มีข้อมูลมากกว่าแค่ title และ subtitle เมื่อใดก็ตามที่มีการส่งอินสแตนซ์ใหม่ของ News ไปยัง Header(news) คอมโพสเซเบิลจะ
คอมโพสใหม่ แม้ว่า title และ subtitle จะไม่เปลี่ยนแปลงก็ตาม
โปรดพิจารณาจํานวนพารามิเตอร์ที่คุณส่งอย่างรอบคอบ การมีฟังก์ชันที่มีพารามิเตอร์มากเกินไปจะลดความสะดวกในการใช้งานของฟังก์ชัน ดังนั้นในกรณีนี้ จึงควรจัดกลุ่มพารามิเตอร์ไว้ในคลาส
เหตุการณ์ในฟีเจอร์เขียน
อินพุตทุกรายการในแอปควรแสดงเป็นเหตุการณ์ เช่น การแตะ การเปลี่ยนแปลงข้อความ
และแม้แต่ตัวจับเวลาหรือการอัปเดตอื่นๆ เนื่องจากเหตุการณ์เหล่านี้จะเปลี่ยนสถานะของ UI
ViewModel จึงควรจัดการเหตุการณ์และอัปเดตสถานะ UI
เลเยอร์ UI ไม่ควรเปลี่ยนสถานะภายนอกตัวแฮนเดิลเหตุการณ์เนื่องจากอาจทำให้เกิดความไม่สอดคล้องกันและข้อบกพร่องในแอปพลิเคชัน
ควรส่งค่าที่เปลี่ยนแปลงไม่ได้สำหรับสถานะและ Lambda ของตัวแฮนเดิลเหตุการณ์ วิธีนี้มีประโยชน์ดังนี้
- คุณปรับปรุงการนำกลับมาใช้ใหม่
- คุณยืนยันว่า UI ไม่ได้เปลี่ยนค่าของสถานะโดยตรง
- คุณหลีกเลี่ยงปัญหาการทำงานพร้อมกันได้เนื่องจากคุณตรวจสอบว่าไม่ได้มีการเปลี่ยนแปลงสถานะจากเธรดอื่น
- ซึ่งมักจะช่วยลดความซับซ้อนของโค้ด
ตัวอย่างเช่น Composable ที่รับ String และ Lambda เป็นพารามิเตอร์จะ
เรียกใช้ได้จากหลายบริบทและนำกลับมาใช้ซ้ำได้สูง สมมติว่าแถบแอปด้านบนในแอปของคุณแสดงข้อความเสมอและมีปุ่มย้อนกลับ คุณสามารถกําหนด MyAppTopAppBar composable
ที่ทั่วไปมากขึ้นซึ่งรับข้อความและแฮนเดิลปุ่มย้อนกลับเป็นพารามิเตอร์ได้
@Composable fun MyAppTopAppBar(topAppBarText: String, onBackPressed: () -> Unit) { TopAppBar( title = { Text( text = topAppBarText, textAlign = TextAlign.Center, modifier = Modifier .fillMaxSize() .wrapContentSize(Alignment.Center) ) }, navigationIcon = { IconButton(onClick = onBackPressed) { Icon( Icons.AutoMirrored.Filled.ArrowBack, contentDescription = localizedString ) } }, // ... ) }
ViewModel, สถานะ และเหตุการณ์: ตัวอย่าง
การใช้ ViewModel และ mutableStateOf ยังช่วยให้คุณนำโฟลว์ข้อมูลแบบทางเดียวมาใช้ในแอปได้ด้วย หากมีเงื่อนไขตรงกับข้อใดข้อหนึ่งต่อไปนี้
- สถานะของ UI จะแสดงโดยใช้ตัวยึดสถานะที่สังเกตได้ เช่น
StateFlowหรือLiveData ViewModelจัดการเหตุการณ์ที่มาจาก UI หรือเลเยอร์อื่นๆ ของแอป และอัปเดตที่เก็บสถานะตามเหตุการณ์
ตัวอย่างเช่น เมื่อติดตั้งใช้งานหน้าจอลงชื่อเข้าใช้ การแตะปุ่มลงชื่อเข้าใช้ ควรทำให้แอปแสดงวงกลมแสดงความคืบหน้าและเรียกใช้เครือข่าย หาก การเข้าสู่ระบบสำเร็จ แอปจะไปยังหน้าจออื่น ในกรณีที่ เกิดข้อผิดพลาด แอปจะแสดงแถบข้อความ วิธีสร้างโมเดลสถานะหน้าจอ และเหตุการณ์
หน้าจอมี 4 สถานะ ได้แก่
- ออกจากระบบ: เมื่อผู้ใช้ยังไม่ได้ลงชื่อเข้าใช้
- กำลังดำเนินการ: เมื่อแอปพยายามลงชื่อเข้าใช้ให้ผู้ใช้โดย ทำการเรียกเครือข่าย
- ข้อผิดพลาด: เมื่อเกิดข้อผิดพลาดขณะลงชื่อเข้าใช้
- ลงชื่อเข้าใช้: เมื่อผู้ใช้ลงชื่อเข้าใช้
คุณสามารถสร้างโมเดลสถานะเหล่านี้เป็นคลาสที่ปิดผนึกได้ ViewModel จะแสดงสถานะ
เป็น State, ตั้งค่าสถานะเริ่มต้น และอัปเดตสถานะตามต้องการ นอกจากนี้
ViewModel ยังจัดการเหตุการณ์การลงชื่อเข้าใช้โดยการเปิดเผยเมธอด onSignIn() ด้วย
class MyViewModel : ViewModel() { private val _uiState = mutableStateOf<UiState>(UiState.SignedOut) val uiState: State<UiState> get() = _uiState // ... }
นอกจาก mutableStateOf API แล้ว Compose ยังมี
ส่วนขยายสําหรับ LiveData, Flow และ Observable เพื่อลงทะเบียนเป็น
เครื่องรับฟังและแสดงค่าเป็นสถานะ
class MyViewModel : ViewModel() { private val _uiState = MutableLiveData<UiState>(UiState.SignedOut) val uiState: LiveData<UiState> get() = _uiState // ... } @Composable fun MyComposable(viewModel: MyViewModel) { val uiState = viewModel.uiState.observeAsState() // ... }
ดูข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับสถาปัตยกรรมใน Jetpack Compose ได้ที่แหล่งข้อมูลต่อไปนี้
ตัวอย่าง
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- สถานะและ Jetpack Compose
- บันทึกสถานะ UI ใน Compose
- จัดการอินพุตของผู้ใช้