การจัดโครงสร้าง UI ของ Compose

UI ใน Compose จะเปลี่ยนแปลงไม่ได้และจะไม่มีการอัปเดตหลังจากอัปเดตแล้ว วาด สิ่งที่คุณควบคุมได้คือสถานะของ UI ทุกครั้งที่สถานะของ การเปลี่ยนแปลง UI, Compose จะสร้างส่วนของโครงสร้าง UI ที่มี มีการเปลี่ยนแปลง Composable ยอมรับได้ สถานะและแสดงเหตุการณ์ เช่น TextField จะยอมรับค่าและแสดง Callback onValueChange ที่ขอเครื่องจัดการ Callback เพื่อเปลี่ยน

var name by remember { mutableStateOf("") }
OutlinedTextField(
    value = name,
    onValueChange = { name = it },
    label = { Text("Name") }
)

เนื่องจาก Composable จะยอมรับสถานะและแสดงเหตุการณ์ โฟลว์ข้อมูลที่เป็นทิศทางเดียวกัน รูปแบบเข้ากันได้ดีกับ Jetpack Compose คู่มือนี้เน้นวิธีติดตั้งใช้งาน รูปแบบโฟลว์ข้อมูลแบบทิศทางเดียวในการเขียน วิธีใช้งานเหตุการณ์ เจ้าของสถานะ และวิธีทำงานกับ ViewModels ใน Compose

การรับส่งข้อมูลแบบทิศทางเดียว

โฟลว์ข้อมูลแบบทิศทางเดียว (UDF) เป็นรูปแบบการออกแบบที่รัฐจะไหลลง และเหตุการณ์ต่างๆ จะไหลลื่นขึ้น เมื่อทำตามโฟลว์ข้อมูลที่ไปในทิศทางเดียวกัน คุณจะแยกส่วน Composable ที่แสดงสถานะใน UI จากส่วนต่างๆ ของแอปที่จัดเก็บ และเปลี่ยนสถานะ

ลูปการอัปเดต UI สำหรับแอปที่ใช้โฟลว์ข้อมูลแบบทิศทางเดียวจะมีลักษณะดังนี้

  • กิจกรรม: ส่วนหนึ่งของ UI จะสร้างกิจกรรมและส่งต่อขึ้นด้านบน เช่น ส่งการคลิกปุ่มไปยัง ViewModel เพื่อจัดการ หรือเหตุการณ์ได้รับการส่งผ่าน อื่นๆ ของแอป เช่น การระบุว่าเซสชันของผู้ใช้ หมดอายุแล้ว
  • สถานะการอัปเดต: เครื่องจัดการเหตุการณ์อาจเปลี่ยนสถานะ
  • สถานะการแสดงผล: เจ้าของสถานะข้ามสถานะไป และ UI จะแสดงขึ้น ได้

รูปที่ 1 การรับส่งข้อมูลแบบทิศทางเดียว

รูปแบบต่อไปนี้เมื่อใช้ Jetpack Compose มีข้อดีหลายประการดังนี้

  • ความสามารถในการทดสอบ: การแยกสถานะจาก UI ที่แสดงช่วยให้ดำเนินการได้ง่ายขึ้น เพื่อทดสอบทั้ง 2 แบบแยกกัน
  • การสรุปสถานะ: เนื่องจากสามารถอัปเดตสถานะได้ในที่เดียวและ มีแหล่งข้อมูลที่ถูกต้องเพียงแหล่งเดียวสำหรับสถานะของ Composable ซึ่งน้อยกว่า คุณอาจจะสร้างข้อบกพร่องจากสถานะที่ไม่สอดคล้องกัน
  • ความสอดคล้องของ UI: การอัปเดตสถานะทั้งหมดจะแสดงใน UI ทันทีโดย การใช้เจ้าของสถานะที่สังเกตได้ เช่น StateFlow หรือ LiveData

การรับส่งข้อมูลแบบทิศทางเดียวใน Jetpack Compose

Composable จะทำงานตามสถานะและเหตุการณ์ ตัวอย่างเช่น TextField จะมีเพียง อัปเดตเมื่ออัปเดตพารามิเตอร์ value และแสดง onValueChange Callback — เหตุการณ์ที่มีการขอให้เปลี่ยนค่าเป็นค่าใหม่ เขียน กำหนดออบเจ็กต์ State เป็นที่เก็บค่า และเปลี่ยนเป็นค่าสถานะ ก็จะทำให้เกิดการจัดองค์ประกอบใหม่ คุณสามารถเก็บสถานะใน remember { mutableStateOf(value) } หรือ rememberSaveable { mutableStateOf(value) ขึ้นอยู่กับระยะเวลาที่ต้องการ ให้จดจำค่าสำหรับ

ประเภทของค่า Composable ของ TextField คือ 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) องค์ประกอบ Composable จะ เขียนใหม่แม้ว่า title และ subtitle จะไม่มีการเปลี่ยนแปลง

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

เหตุการณ์ใน Compose

การป้อนข้อมูลทุกรายการในแอปควรแสดงเป็นเหตุการณ์ เช่น การแตะ การเปลี่ยนแปลงข้อความ และแม้แต่ตัวจับเวลาหรือการอัปเดตอื่นๆ เมื่อเหตุการณ์เหล่านี้เปลี่ยนสถานะ 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.Filled.ArrowBack,
                    contentDescription = localizedString
                )
            }
        },
        // ...
    )
}

ViewModels, สถานะ และเหตุการณ์: ตัวอย่าง

การใช้ 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

    // ...
}

นอกเหนือจาก mutableStateOf API แล้ว 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 โปรดดูแหล่งข้อมูลต่อไปนี้

ตัวอย่าง