State และ Jetpack Compose

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

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

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

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

สถานะและการคอมโพส

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

@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 เปลี่ยน ซึ่งเป็นผลมาจากการทำงานของ Composition และการจัดองค์ประกอบใหม่ใน Compose

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

สถานะใน Composable

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

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

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

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

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

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

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

ไวยากรณ์ตัวแทน by ต้องมีการนำเข้าต่อไปนี้

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

คุณสามารถใช้ค่าที่จดจำไว้เป็นพารามิเตอร์สำหรับ Composable อื่นๆ หรือแม้แต่เป็นตรรกะในคำสั่งเพื่อเปลี่ยน Composable ที่แสดง เช่น หากไม่ต้องการแสดงคำทักทายหากชื่อว่าง ให้ใช้สถานะในคำสั่ง 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 โดยอัตโนมัติ สำหรับค่าอื่นๆ คุณสามารถส่งออบเจ็กต์ Saver ที่กำหนดเองได้

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

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

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

  • Flow: collectAsStateWithLifecycle()

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

    คุณต้องมีทรัพยากร Dependency ต่อไปนี้ในไฟล์ build.gradle (ควร เป็น 2.6.0-beta01 ขึ้นไป)

Kotlin

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

ดึงดูด

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

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

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

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

  • LiveData: observeAsState()

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

    คุณต้องมีทรัพยากร Dependency ต่อไปนี้ในไฟล์ build.gradle

Kotlin

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

ดึงดูด

dependencies {
      ...
      implementation "androidx.compose.runtime:runtime-livedata:1.11.1"
}
  • RxJava2: subscribeAsState()

    subscribeAsState() เป็นฟังก์ชันส่วนขยายที่แปลงสตรีมแบบโต้ตอบของ RxJava2 (เช่น Single, Observable, Completable) เป็น State ของ Compose

    คุณต้องมีทรัพยากร Dependency ต่อไปนี้ในไฟล์ build.gradle

Kotlin

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

ดึงดูด

dependencies {
      ...
      implementation "androidx.compose.runtime:runtime-rxjava2:1.11.1"
}
  • RxJava3: subscribeAsState()

    subscribeAsState() เป็นฟังก์ชันส่วนขยายที่แปลงสตรีมแบบโต้ตอบของ RxJava3 (เช่น Single, Observable, Completable) เป็น State ของ Compose

    คุณต้องมีทรัพยากร Dependency ต่อไปนี้ในไฟล์ build.gradle

Kotlin

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

ดึงดูด

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

เก็บสถานะเทียบกับไม่เก็บสถานะ

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

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

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

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

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

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

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

สถานะที่ย้ายด้วยวิธีนี้มีคุณสมบัติที่สำคัญบางประการ ดังนี้

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

ในกรณีตัวอย่างนี้ คุณจะแยก name และ onValueChange ออกจาก HelloContent และย้ายขึ้นไปในโครงสร้างเป็น Composable 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 จะช่วยให้คุณพิจารณา Composable ได้ง่ายขึ้น นำกลับมาใช้ซ้ำในสถานการณ์ต่างๆ ได้ง่ายขึ้น และทดสอบได้ง่ายขึ้น HelloContent จะแยกออกจากวิธีจัดเก็บสถานะ การแยกออกจากกันหมายความว่าหากคุณแก้ไขหรือแทนที่ HelloScreen คุณก็ไม่จำเป็นต้องเปลี่ยนวิธีใช้งาน HelloContent

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

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

การกู้คืนสถานะใน Compose

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

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

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

Parcelize

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

@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

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

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

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

API remember มักใช้ร่วมกับ MutableState

var name by remember { mutableStateOf("") }

ในที่นี้ การใช้ฟังก์ชัน remember จะทำให้ค่า MutableState ยังคงอยู่ตลอดการคอมโพสใหม่

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

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

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

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

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

ในข้อมูลโค้ดนี้ ระบบจะสร้าง ShaderBrush และใช้เป็นสีพื้นหลัง ของ Composable 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 ของคลาสเพื่อพิจารณาว่าคีย์มีการ เปลี่ยนแปลงหรือไม่ และล้างค่าที่จัดเก็บไว้

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

API rememberSaveable เป็น 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 ได้จากแหล่งข้อมูลเพิ่มเติมต่อไปนี้

ตัวอย่าง

Codelab

วิดีโอ

บล็อก