State และ Jetpack Compose

สถานะในแอปคือค่าใดก็ได้ที่เปลี่ยนแปลงได้เมื่อเวลาผ่านไป คำจำกัดความนี้กว้างมากและครอบคลุมตั้งแต่ฐานข้อมูล Room ไปจนถึงตัวแปรในคลาส

แอป Android ทั้งหมดจะแสดงสถานะต่อผู้ใช้ ตัวอย่างสถานะใน Android App

  • แซนบาร์ที่แสดงเมื่อเชื่อมต่อเครือข่ายไม่ได้
  • บล็อกโพสต์และความคิดเห็นที่เกี่ยวข้อง
  • ภาพเคลื่อนไหวแบบกระเพื่อมบนปุ่มที่เล่นเมื่อผู้ใช้คลิก
  • สติกเกอร์ที่ผู้ใช้วาดทับรูปภาพได้

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

สถานะและองค์ประกอบ

คอมโพสิชันเป็นแบบประกาศ ดังนั้นวิธีเดียวที่จะอัปเดตได้คือเรียกใช้คอมโพสิชันเดียวกันด้วยอาร์กิวเมนต์ใหม่ โดยอาร์กิวเมนต์เหล่านี้แสดงสถานะของ UI ทุกครั้งที่มีการอัปเดตสถานะ ระบบจะจัดองค์ประกอบใหม่ ด้วยเหตุนี้ องค์ประกอบอย่าง TextField จึงจะไม่อัปเดตโดยอัตโนมัติเหมือนในมุมมองแบบบังคับซึ่งอิงตาม XML คอมโพสิเบิลต้องได้รับการบอกสถานะใหม่อย่างชัดเจนเพื่อให้อัปเดตตามนั้น

@Composable
private fun HelloContent() {
    Column(modifier = Modifier.padding(16.dp)) {
        Text(
            text = "Hello!",
            modifier = Modifier.padding(bottom = 8.dp),
            style = MaterialTheme.typography.bodyMedium
        )
        OutlinedTextField(
            value = "",
            onValueChange = { },
            label = { Text("Name") }
        )
    }
}

หากเรียกใช้และพยายามป้อนข้อความ คุณจะเห็นว่าไม่มีอะไรเกิดขึ้น เนื่องจาก TextField จะไม่อัปเดตเอง แต่อัปเดตเมื่อพารามิเตอร์ value เปลี่ยนแปลง ปัญหานี้เกิดจากวิธีการทำงานของการจัดองค์ประกอบและการจัดองค์ประกอบใหม่ใน Compose

ดูข้อมูลเพิ่มเติมเกี่ยวกับการเขียนแบบร่างครั้งแรกและการเขียนแบบร่างอีกครั้งได้ที่การคิดในเครื่องมือเขียน

สถานะในคอมโพสิเบิล

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

mutableStateOf สร้าง Observable MutableState<T> ซึ่งเป็นประเภท Observable ที่ผสานรวมกับรันไทม์ของ Compose

interface MutableState<T> : State<T> {
    override var value: T
}

การเปลี่ยนแปลงใดๆ ใน value จะกำหนดเวลาการจัดเรียงใหม่ของฟังก์ชันแบบคอมโพสิเบิลที่อ่าน value

การประกาศออบเจ็กต์ MutableState ในคอมโพสิเบิลทำได้ 3 วิธีดังนี้

  • val mutableState = remember { mutableStateOf(default) }
  • var value by remember { mutableStateOf(default) }
  • val (value, setValue) = remember { mutableStateOf(default) }

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

ไวยากรณ์ by ของ Delegate ต้องมีการนําเข้าต่อไปนี้

import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue

คุณสามารถใช้ค่าที่บันทึกไว้เป็นพารามิเตอร์สําหรับคอมโพสิเบิลอื่นๆ หรือแม้แต่เป็นตรรกะในคำสั่งเพื่อเปลี่ยนคอมโพสิเบิลที่จะแสดง ตัวอย่างเช่น หากไม่ต้องการให้แสดงคําทักทายหากชื่อว่างเปล่า ให้ใช้สถานะในifคำสั่ง ดังนี้

@Composable
fun HelloContent() {
    Column(modifier = Modifier.padding(16.dp)) {
        var name by remember { mutableStateOf("") }
        if (name.isNotEmpty()) {
            Text(
                text = "Hello, $name!",
                modifier = Modifier.padding(bottom = 8.dp),
                style = MaterialTheme.typography.bodyMedium
            )
        }
        OutlinedTextField(
            value = name,
            onValueChange = { name = it },
            label = { Text("Name") }
        )
    }
}

แม้ว่า remember จะช่วยรักษาสถานะไว้ระหว่างการคอมโพสใหม่ แต่สถานะดังกล่าวจะไม่ได้รับการเก็บรักษาไว้เมื่อมีการทําการเปลี่ยนแปลงการกําหนดค่า ในกรณีนี้ คุณต้องใช้ rememberSaveable rememberSaveable จะบันทึกค่าที่บันทึกได้ใน Bundle โดยอัตโนมัติ สําหรับค่าอื่นๆ คุณสามารถส่งออบเจ็กต์โปรแกรมช่วยบันทึกที่กําหนดเองได้

สถานะประเภทอื่นๆ ที่รองรับ

คอมโพซไม่จําเป็นต้องใช้ MutableState<T> เพื่อเก็บสถานะ แต่รองรับประเภทอื่นๆ ที่สังเกตได้ ก่อนอ่านประเภทที่สังเกตได้อื่นใน Compose คุณต้องแปลงเป็น State<T> เพื่อให้คอมโพสิเบิลคอมไพล์ใหม่โดยอัตโนมัติเมื่อสถานะมีการเปลี่ยนแปลง

คอมโพสิชันเรือกับฟังก์ชันเพื่อสร้าง State<T> จากประเภทที่สังเกตได้ทั่วไปซึ่งใช้ในแอป Android ก่อนใช้การผสานรวมเหล่านี้ ให้เพิ่มอาร์ติแฟกต์ที่เหมาะสมตามที่ระบุไว้ด้านล่าง

  • Flow: collectAsStateWithLifecycle()

    collectAsStateWithLifecycle() จะรวบรวมค่าจาก Flow ในลักษณะที่คำนึงถึงวงจร ทำให้แอปของคุณประหยัดทรัพยากรได้ ค่านี้แสดงถึงค่าที่ส่งออกล่าสุดจาก State ของคอมโพสิชัน ใช้ API นี้เป็นวิธีที่แนะนําในการรวบรวมการไหลเวียนในแอป Android

    ต้องมีข้อกําหนดเบื้องต้นต่อไปนี้ในไฟล์ build.gradle (ควรเป็น 2.6.0-beta01 ขึ้นไป)

Kotlin

dependencies {
      ...
      implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.7")
}

Groovy

dependencies {
      ...
      implementation "androidx.lifecycle:lifecycle-runtime-compose:2.8.7"
}
  • Flow: collectAsState()

    collectAsState คล้ายกับ collectAsStateWithLifecycle เนื่องจากจะรวบรวมค่าจาก Flow และเปลี่ยนค่าดังกล่าวเป็น Compose State ด้วย

    ใช้ collectAsState สำหรับโค้ดที่ไม่ขึ้นอยู่กับแพลตฟอร์มแทน collectAsStateWithLifecycle ซึ่งใช้ได้กับ Android เท่านั้น

    collectAsState ไม่จำเป็นต้องใช้ทรัพยากรเพิ่มเติมเนื่องจากมีอยู่ใน compose-runtime

  • LiveData: observeAsState()

    observeAsState() เริ่มสังเกต LiveData นี้และแสดงค่าผ่าน State

    ต้องมีข้อกําหนดเบื้องต้นต่อไปนี้ในไฟล์ build.gradle

Kotlin

dependencies {
      ...
      implementation("androidx.compose.runtime:runtime-livedata:1.7.5")
}

Groovy

dependencies {
      ...
      implementation "androidx.compose.runtime:runtime-livedata:1.7.5"
}

Kotlin

dependencies {
      ...
      implementation("androidx.compose.runtime:runtime-rxjava2:1.7.5")
}

Groovy

dependencies {
      ...
      implementation "androidx.compose.runtime:runtime-rxjava2:1.7.5"
}

Kotlin

dependencies {
      ...
      implementation("androidx.compose.runtime:runtime-rxjava3:1.7.5")
}

Groovy

dependencies {
      ...
      implementation "androidx.compose.runtime:runtime-rxjava3:1.7.5"
}

การเก็บสถานะกับการไม่เก็บสถานะ

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

คอมโพสิเบิลที่ไม่มีสถานะคือคอมโพสิเบิลที่ไม่มีสถานะใดๆ วิธีง่ายๆ ในการทำให้เป็นแบบไม่เก็บสถานะคือการใช้การยกสถานะ

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

การย้ายสถานะ

การยกระดับสถานะใน Compose เป็นรูปแบบการย้ายสถานะไปยังผู้เรียกของคอมโพสิเบิลเพื่อทำให้เป็นคอมโพสิเบิลที่ไม่มีสถานะ รูปแบบทั่วไปสำหรับการยกระดับสถานะใน Compose ของ Jetpack คือการใช้พารามิเตอร์ 2 รายการแทนตัวแปรสถานะ ดังนี้

  • value: T: ค่าปัจจุบันที่จะแสดง
  • onValueChange: (T) -> Unit: เหตุการณ์ที่ขอให้เปลี่ยนค่า โดยที่ T คือค่าใหม่ที่เสนอ

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

สถานะที่ยกระดับด้วยวิธีนี้มีพร็อพเพอร์ตี้สำคัญบางอย่างดังนี้

  • แหล่งข้อมูลที่เชื่อถือได้เพียงแหล่งเดียว: การย้ายสถานะแทนการทำซ้ำช่วยให้มั่นใจได้ว่าจะมีแหล่งข้อมูลที่เชื่อถือได้เพียงแหล่งเดียว วิธีนี้จะช่วยหลีกเลี่ยงข้อบกพร่อง
  • Encapsulated: มีเพียงคอมโพสิเบิลที่มีสถานะเท่านั้นที่จะแก้ไขสถานะได้ การดำเนินการนี้เกิดขึ้นภายใน
  • แชร์ได้: แชร์สถานะที่มีการยกระดับกับคอมโพสิเบิลหลายรายการได้ หากต้องการอ่าน name ในคอมโพสิเบิลอื่น การยกระดับจะช่วยให้คุณทําเช่นนั้นได้
  • สามารถรับมือได้: ผู้เรียกใช้คอมโพสิเบิลแบบไม่มีสถานะสามารถเลือกที่จะเพิกเฉยหรือแก้ไขเหตุการณ์ก่อนที่จะเปลี่ยนสถานะได้
  • แยกส่วน: สถานะของคอมโพสิเบิลที่ไม่มีสถานะอาจจัดเก็บไว้ที่ใดก็ได้ ตัวอย่างเช่น ตอนนี้คุณสามารถย้าย name ไปยัง ViewModel ได้แล้ว

ในตัวอย่างนี้ คุณจะดึง name และ onValueChange ออกจาก HelloContent แล้วย้ายขึ้นไปที่คอมโพสิเบิล HelloScreen ที่เรียก HelloContent

@Composable
fun HelloScreen() {
    var name by rememberSaveable { mutableStateOf("") }

    HelloContent(name = name, onNameChange = { name = it })
}

@Composable
fun HelloContent(name: String, onNameChange: (String) -> Unit) {
    Column(modifier = Modifier.padding(16.dp)) {
        Text(
            text = "Hello, $name",
            modifier = Modifier.padding(bottom = 8.dp),
            style = MaterialTheme.typography.bodyMedium
        )
        OutlinedTextField(value = name, onValueChange = onNameChange, label = { Text("Name") })
    }
}

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

รูปแบบที่สถานะลดลงและเหตุการณ์เพิ่มขึ้นเรียกว่าการไหลของข้อมูลแบบทิศทางเดียว ในกรณีนี้ สถานะจะลดลงจาก HelloScreen เป็น HelloContent และเหตุการณ์จะเพิ่มขึ้นจาก HelloContent เป็น HelloScreen เมื่อใช้การไหลของข้อมูลแบบทิศทางเดียว คุณจะแยกคอมโพสิเบิลที่แสดงสถานะใน UI ออกจากส่วนต่างๆ ของแอปที่เก็บและเปลี่ยนแปลงสถานะได้

ดูข้อมูลเพิ่มเติมที่หน้าตําแหน่งที่จะแสดงสถานะ

การกู้คืนสถานะในคอมโพสิท

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

วิธีจัดเก็บสถานะ

ระบบจะบันทึกประเภทข้อมูลทั้งหมดที่เพิ่มลงใน Bundle โดยอัตโนมัติ หากต้องการบันทึกสิ่งที่เพิ่มลงใน Bundle ไม่ได้ คุณมีตัวเลือกหลายอย่าง

แบ่งแปลงที่ดิน

วิธีที่ง่ายที่สุดคือเพิ่มคำอธิบายประกอบ @Parcelize ให้กับออบเจ็กต์ ออบเจ็กต์จะแบ่งออกเป็นส่วนๆ ได้และสามารถรวมกลุ่มได้ ตัวอย่างเช่น โค้ดนี้จะสร้างประเภทข้อมูล City ที่แบ่งออกเป็นส่วนๆ และบันทึกลงในสถานะ

@Parcelize
data class City(val name: String, val country: String) : Parcelable

@Composable
fun CityScreen() {
    var selectedCity = rememberSaveable {
        mutableStateOf(City("Madrid", "Spain"))
    }
}

MapSaver

หาก @Parcelize ไม่เหมาะด้วยเหตุผลบางประการ คุณสามารถใช้ mapSaver เพื่อกำหนดกฎของคุณเองสำหรับการแปลงออบเจ็กต์เป็นชุดค่าที่ระบบจะบันทึกลงใน Bundle ได้

data class City(val name: String, val country: String)

val CitySaver = run {
    val nameKey = "Name"
    val countryKey = "Country"
    mapSaver(
        save = { mapOf(nameKey to it.name, countryKey to it.country) },
        restore = { City(it[nameKey] as String, it[countryKey] as String) }
    )
}

@Composable
fun CityScreen() {
    var selectedCity = rememberSaveable(stateSaver = CitySaver) {
        mutableStateOf(City("Madrid", "Spain"))
    }
}

ListSaver

หากไม่ต้องการกำหนดคีย์สําหรับแผนที่ คุณสามารถใช้ listSaver และใช้ดัชนีเป็นคีย์ได้ ดังนี้

data class City(val name: String, val country: String)

val CitySaver = listSaver<City, Any>(
    save = { listOf(it.name, it.country) },
    restore = { City(it[0] as String, it[1] as String) }
)

@Composable
fun CityScreen() {
    var selectedCity = rememberSaveable(stateSaver = CitySaver) {
        mutableStateOf(City("Madrid", "Spain"))
    }
}

ตัวเก็บสถานะในคอมโพซ

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

ดูข้อมูลเพิ่มเติมได้ในเอกสารประกอบเกี่ยวกับการยกระดับสถานะใน Compose หรือดูข้อมูลทั่วไปได้ในหน้าตัวเก็บสถานะและสถานะ UI ในคู่มือสถาปัตยกรรม

เรียกใช้การคํานวณการจดจําอีกครั้งเมื่อคีย์มีการเปลี่ยนแปลง

remember API มักใช้ร่วมกับ MutableState ดังนี้

var name by remember { mutableStateOf("") }

ในตัวอย่างนี้ การใช้ฟังก์ชัน remember ทําให้ค่า MutableState อยู่รอดได้แม้จะมีการเปลี่ยนรูปแบบ

โดยทั่วไป remember จะใช้พารามิเตอร์ Lambda calculation เมื่อเรียกใช้ remember เป็นครั้งแรก ระบบจะเรียกใช้ Lambda ของ calculation และจัดเก็บผลลัพธ์ ในระหว่างการจัดเรียงใหม่ remember จะแสดงค่าที่เก็บไว้ล่าสุด

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

val brush = remember {
    ShaderBrush(
        BitmapShader(
            ImageBitmap.imageResource(res, avatarRes).asAndroidBitmap(),
            Shader.TileMode.REPEAT,
            Shader.TileMode.REPEAT
        )
    )
}

remember จะจัดเก็บค่าไว้จนกว่าค่านั้นจะออกจากการคอมโพสิชัน อย่างไรก็ตาม มีวิธีทำให้ค่าที่แคชไว้เป็นค่าที่ไม่ถูกต้อง นอกจากนี้ remember API ยังใช้พารามิเตอร์ key หรือ keys ด้วย หากคีย์ใดคีย์หนึ่งเหล่านี้มีการเปลี่ยนแปลง ครั้งถัดไปที่ฟังก์ชันคอมไพล์ใหม่ remember จะลบล้างแคชและดำเนินการคำนวณในบล็อก lambda อีกครั้ง กลไกนี้ช่วยให้คุณควบคุมอายุการใช้งานของออบเจ็กต์ในคอมโพสิชันได้ การคํานวณจะยังคงใช้งานได้จนกว่าอินพุตจะเปลี่ยนแปลง ไม่ใช่จนกว่าค่าที่จดจําไว้จะออกจากการคอมโพสิชัน

ตัวอย่างต่อไปนี้แสดงวิธีการทํางานของกลไกนี้

ในข้อมูลโค้ดนี้ ระบบจะสร้าง ShaderBrush และใช้เป็นการวาดพื้นหลังของคอมโพสิเบิล Box remember จะจัดเก็บอินสแตนซ์ ShaderBrush ไว้เนื่องจากการสร้างใหม่มีค่าใช้จ่ายสูง ตามที่อธิบายไว้ก่อนหน้านี้ remember ใช้ avatarRes เป็นพารามิเตอร์ key1 ซึ่งเป็นภาพพื้นหลังที่เลือก หากavatarResมีการเปลี่ยนแปลง แปรงจะจัดองค์ประกอบใหม่ด้วยรูปภาพใหม่และนำไปใช้กับBoxอีกครั้ง กรณีนี้อาจเกิดขึ้นเมื่อผู้ใช้เลือกรูปภาพอื่นเป็นพื้นหลังจากเครื่องมือเลือก

@Composable
private fun BackgroundBanner(
    @DrawableRes avatarRes: Int,
    modifier: Modifier = Modifier,
    res: Resources = LocalContext.current.resources
) {
    val brush = remember(key1 = avatarRes) {
        ShaderBrush(
            BitmapShader(
                ImageBitmap.imageResource(res, avatarRes).asAndroidBitmap(),
                Shader.TileMode.REPEAT,
                Shader.TileMode.REPEAT
            )
        )
    }

    Box(
        modifier = modifier.background(brush)
    ) {
        /* ... */
    }
}

ในข้อมูลโค้ดต่อไปนี้ ระบบจะยกสถานะไปยังคลาสตัวเก็บสถานะแบบธรรมดา MyAppState โดยจะแสดงฟังก์ชัน rememberMyAppState เพื่อเริ่มต้นอินสแตนซ์ของคลาสโดยใช้ remember การแสดงฟังก์ชันดังกล่าวเพื่อสร้างอินสแตนซ์ที่ยังคงอยู่หลังจากการคอมโพสิชันใหม่เป็นรูปแบบที่พบได้ทั่วไปใน Compose ฟังก์ชัน rememberMyAppState ได้รับ windowSizeClass ซึ่งทำหน้าที่เป็นพารามิเตอร์ key สำหรับ remember หากพารามิเตอร์นี้เปลี่ยนแปลง แอปจะต้องสร้างคลาสตัวเก็บสถานะแบบธรรมดาขึ้นมาใหม่โดยใช้ค่าล่าสุด กรณีนี้อาจเกิดขึ้นได้หากผู้ใช้หมุนอุปกรณ์ เป็นต้น

@Composable
private fun rememberMyAppState(
    windowSizeClass: WindowSizeClass
): MyAppState {
    return remember(windowSizeClass) {
        MyAppState(windowSizeClass)
    }
}

@Stable
class MyAppState(
    private val windowSizeClass: WindowSizeClass
) { /* ... */ }

Compose ใช้การใช้งาน equals ของคลาสเพื่อตัดสินใจว่าคีย์มีการเปลี่ยนแปลงหรือไม่และทำให้ค่าที่เก็บไว้เป็นโมฆะ

จัดเก็บสถานะด้วยคีย์นอกเหนือจากการจัดเรียงใหม่

rememberSaveable API คือ Wrapper ของ remember ที่สามารถจัดเก็บข้อมูลใน Bundle API นี้ช่วยให้สถานะอยู่รอดได้ไม่เพียงต่อการคอมโพสิชันใหม่เท่านั้น แต่ยังรวมถึงการสร้างกิจกรรมใหม่และการสิ้นสุดกระบวนการที่ระบบเริ่ม rememberSaveable ได้รับพารามิเตอร์ input เพื่อวัตถุประสงค์เดียวกันกับที่ remember ได้รับ keys แคชจะใช้งานไม่ได้เมื่อข้อมูลใดๆ เปลี่ยนแปลง เมื่อฟังก์ชันคอมไพล์ใหม่ครั้งถัดไป rememberSaveable จะเรียกใช้บล็อก LAMBDA การคํานวณอีกครั้ง

ในตัวอย่างต่อไปนี้ rememberSaveable จะจัดเก็บ userTypedQuery จนกว่า typedQuery จะเปลี่ยนแปลง

var userTypedQuery by rememberSaveable(typedQuery, stateSaver = TextFieldValue.Saver) {
    mutableStateOf(
        TextFieldValue(text = typedQuery, selection = TextRange(typedQuery.length))
    )
}

ดูข้อมูลเพิ่มเติม

ดูข้อมูลเพิ่มเติมเกี่ยวกับสถานะและ Jetpack Compose ได้จากแหล่งข้อมูลเพิ่มเติมต่อไปนี้

ตัวอย่าง

Codelabs

วิดีโอ

บล็อก