ใน Compose UI จะเปลี่ยนแปลงไม่ได้ คุณจะอัปเดต UI หลังจากที่วาดแล้วไม่ได้ สิ่งที่คุณควบคุมได้คือสถานะของ UI ทุกครั้งที่สถานะของ
UI เปลี่ยนไป Compose จะสร้างส่วนต่างๆ ของแผนผัง UI ที่มีการ
เปลี่ยนแปลงขึ้นมาใหม่ Composables สามารถยอมรับสถานะและแสดงเหตุการณ์ได้ เช่น TextField ยอมรับค่าและแสดง Callback onValueChange ที่ขอให้ Callback Handler เปลี่ยนค่า
var name by remember { mutableStateOf("") } OutlinedTextField( value = name, onValueChange = { name = it }, label = { Text("Name") } )
เนื่องจาก Composables ยอมรับสถานะและแสดงเหตุการณ์ รูปแบบโฟลว์ข้อมูลแบบทิศทางเดียวจึงเหมาะกับ Jetpack Compose คำแนะนำนี้จะเน้นที่วิธีใช้รูปแบบโฟลว์ข้อมูลแบบทิศทางเดียวใน Compose, วิธีใช้ตัวเก็บสถานะและเหตุการณ์ และวิธีใช้ ViewModel ใน Compose
โฟลว์ข้อมูลแบบทิศทางเดียว
โฟลว์ข้อมูลแบบทิศทางเดียว (UDF) เป็นรูปแบบการออกแบบที่สถานะไหลลงและเหตุการณ์ไหลขึ้น การทำตามโฟลว์ข้อมูลแบบทิศทางเดียวจะช่วยให้คุณแยก Composables ที่แสดงสถานะใน UI ออกจากส่วนต่างๆ ของแอปที่จัดเก็บและเปลี่ยนสถานะได้
วงจรการอัปเดต UI สำหรับแอปที่ใช้โฟลว์ข้อมูลแบบทิศทางเดียวมีลักษณะดังนี้
- เหตุการณ์: ส่วนหนึ่งของ UI สร้างเหตุการณ์และส่งต่อขึ้นไป เช่น การ คลิกปุ่มที่ส่งไปยัง ViewModel เพื่อจัดการ หรือเหตุการณ์ที่ส่งจาก เลเยอร์อื่นๆ ของแอป เช่น การระบุว่าเซสชันของผู้ใช้หมดอายุ แล้ว
- อัปเดตสถานะ: ตัวจัดการเหตุการณ์อาจเปลี่ยนสถานะ
- แสดงสถานะ: ตัวเก็บสถานะจะส่งสถานะลงมา และ UI แสดงสถานะดังกล่าว
การทำตามรูปแบบนี้เมื่อใช้ Jetpack Compose มีข้อดีหลายประการดังนี้
- ความสามารถในการทดสอบ: การแยกสถานะออกจาก UI ที่แสดงสถานะทำให้ทดสอบได้ง่ายขึ้น ทั้งแบบแยกกัน
- การห่อหุ้มสถานะ: เนื่องจากสถานะอัปเดตได้ในที่เดียวเท่านั้นและมีแหล่งข้อมูลที่เชื่อถือได้เพียงแหล่งเดียวสำหรับสถานะของ Composable จึงมีโอกาสน้อยที่จะเกิดข้อบกพร่องเนื่องจากสถานะไม่สอดคล้องกัน
- ความสอดคล้องของ UI: การอัปเดตสถานะทั้งหมดจะแสดงใน UI ทันทีโดย
ใช้ตัวเก็บสถานะที่สังเกตได้ เช่น
StateFlowหรือLiveData
โฟลว์ข้อมูลแบบทิศทางเดียวใน Jetpack Compose
Composables ทำงานโดยอิงตามสถานะและเหตุการณ์ ตัวอย่างเช่น TextField จะอัปเดตก็ต่อเมื่อพารามิเตอร์ value อัปเดตแล้ว และจะแสดงการเรียกกลับ onValueChange ซึ่งเป็นเหตุการณ์ที่ขอให้เปลี่ยนค่าเป็นค่าใหม่ Compose กำหนดออบเจ็กต์ State เป็นตัวเก็บค่า และการเปลี่ยนแปลงค่าสถานะจะทริกเกอร์การจัดองค์ประกอบใหม่ คุณสามารถเก็บสถานะไว้ใน remember { mutableStateOf(value) } หรือ rememberSaveable { mutableStateOf(value) } ทั้งนี้ขึ้นอยู่กับระยะเวลาที่คุณต้องการจดจำค่า
ประเภทของค่า Composables TextField คือ String ซึ่งอาจมาจากที่ใดก็ได้ ไม่ว่าจะเป็นค่าที่ฮาร์ดโค้ด, จาก ViewModel หรือส่งมาจาก Composables ระดับบน คุณไม่จำเป็นต้องเก็บค่าไว้ในออบเจ็กต์ State แต่ต้องอัปเดตค่าเมื่อมีการเรียก onValueChange
กำหนดพารามิเตอร์ Composables
เมื่อกำหนดพารามิเตอร์สถานะของ Composables ให้คำนึงถึงคำถามต่อไปนี้
- Composables สามารถนำกลับมาใช้ซ้ำหรือยืดหยุ่นได้มากน้อยเพียงใด
- พารามิเตอร์สถานะส่งผลต่อประสิทธิภาพของ Composables นี้อย่างไร
เพื่อส่งเสริมการแยกและการนำกลับมาใช้ซ้ำ Composables แต่ละรายการควรเก็บข้อมูลให้น้อยที่สุด ตัวอย่างเช่น เมื่อสร้าง Composables เพื่อเก็บส่วนหัวของบทความข่าว ให้ส่งเฉพาะข้อมูลที่ต้องแสดงแทนที่จะส่งบทความข่าวทั้งหมด
@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) Composables จะสร้างใหม่
แม้ว่า title และ subtitle จะไม่เปลี่ยนแปลงก็ตาม
พิจารณาจำนวนพารามิเตอร์ที่คุณส่งอย่างรอบคอบ การมีฟังก์ชันที่มีพารามิเตอร์มากเกินไปจะลดการยศาสตร์ของฟังก์ชัน ดังนั้นในกรณีนี้จึงควรจัดกลุ่มพารามิเตอร์ไว้ในคลาส
เหตุการณ์ใน Compose
อินพุตทั้งหมดที่ส่งไปยังแอปควรแสดงเป็นเหตุการณ์ ไม่ว่าจะเป็นการแตะ การเปลี่ยนแปลงข้อความ หรือแม้แต่ตัวจับเวลาหรือการอัปเดตอื่นๆ เนื่องจากเหตุการณ์เหล่านี้เปลี่ยนสถานะของ UI ดังนั้น ViewModel ควรจัดการเหตุการณ์และอัปเดตสถานะ UI
เลเยอร์ UI ไม่ควรเปลี่ยนสถานะนอกตัวจัดการเหตุการณ์ เนื่องจากอาจทำให้เกิดความไม่สอดคล้องกันและข้อบกพร่องในแอปพลิเคชัน
ควรส่งค่าที่เปลี่ยนแปลงไม่ได้สำหรับสถานะและแลมบ์ดาตัวจัดการเหตุการณ์ แนวทางนี้มีประโยชน์ดังนี้
- คุณปรับปรุงความสามารถในการนำกลับมาใช้ซ้ำ
- คุณตรวจสอบว่า UI ไม่ได้เปลี่ยนค่าสถานะโดยตรง
- คุณหลีกเลี่ยงปัญหาการทำงานพร้อมกันได้เนื่องจากคุณตรวจสอบว่าสถานะไม่ได้เปลี่ยนแปลงจากเทรดอื่น
- คุณลดความซับซ้อนของโค้ดได้บ่อยครั้ง
ตัวอย่างเช่น Composables ที่ยอมรับ String และแลมบ์ดาเป็นพารามิเตอร์สามารถเรียกจากบริบทต่างๆ ได้มากมายและนำกลับมาใช้ซ้ำได้สูง สมมติว่าแถบแอปด้านบนในแอปของคุณแสดงข้อความเสมอและมีปุ่มย้อนกลับ คุณสามารถกำหนด Composables MyAppTopAppBar ที่ทั่วไปมากขึ้นซึ่งรับข้อความและตัวจัดการปุ่มย้อนกลับเป็นพารามิเตอร์ได้ดังนี้
@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 หรือเลเยอร์อื่นๆ ของแอป และอัปเดตตัวเก็บสถานะตามเหตุการณ์
ตัวอย่างเช่น เมื่อใช้หน้าจอลงชื่อเข้าใช้ การแตะปุ่มลงชื่อเข้าใช้ ควรทำให้แอปแสดงตัวหมุนความคืบหน้าและการเรียกเครือข่าย หากการเข้าสู่ระบบสำเร็จ แอปจะไปยังหน้าจออื่น แต่หากเกิดข้อผิดพลาด แอปจะแสดง Snackbar วิธีสร้างแบบจำลองสถานะหน้าจอและเหตุการณ์มีดังนี้
หน้าจอมี 4 สถานะ ได้แก่
- ลงชื่อออก: เมื่อผู้ใช้ยังไม่ได้ลงชื่อเข้าใช้
- กำลังดำเนินการ: เมื่อแอปพยายามลงชื่อเข้าใช้ให้ผู้ใช้โดย ทำการเรียกเครือข่าย
- ข้อผิดพลาด: เมื่อเกิดข้อผิดพลาดขณะลงชื่อเข้าใช้
- ลงชื่อเข้าใช้แล้ว: เมื่อผู้ใช้ลงชื่อเข้าใช้แล้ว
คุณสามารถสร้างแบบจำลองสถานะเหล่านี้เป็นคลาสที่ปิดผนึก ViewModel จะแสดงสถานะเป็น State ตั้งค่าสถานะเริ่มต้น และอัปเดตสถานะตามความจำเป็น ViewModel ยังจัดการเหตุการณ์ลงชื่อเข้าใช้โดยแสดงเมธอด onSignIn() ด้วย
class MyViewModel : ViewModel() { private val _uiState = mutableStateOf<UiState>(UiState.SignedOut) val uiState: State<UiState> get() = _uiState // ... }
นอกจาก API mutableStateOf แล้ว Compose มี ส่วนขยาย สำหรับ LiveData, Flow, และ Observable เพื่อลงทะเบียนเป็น Listener และแสดงค่าเป็นสถานะ
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
- จัดการข้อมูลจากผู้ใช้