เขียนและไลบรารีอื่นๆ

คุณใช้คลังเพลงโปรดใน "เขียน" ได้ ส่วนนี้จะอธิบายวิธีใช้ไลบรารีที่มีประโยชน์ที่สุด 2-3 รายการ

กิจกรรม

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

ผลลัพธ์ของกิจกรรม

rememberLauncherForActivityResult() API นี้ช่วยให้คุณรับผลลัพธ์จากกิจกรรมในคอมโพสิเบิลได้ ดังนี้

@Composable
fun GetContentExample() {
    var imageUri by remember { mutableStateOf<Uri?>(null) }
    val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
        imageUri = uri
    }
    Column {
        Button(onClick = { launcher.launch("image/*") }) {
            Text(text = "Load Image")
        }
        Image(
            painter = rememberAsyncImagePainter(imageUri),
            contentDescription = "My Image"
        )
    }
}

ตัวอย่างนี้แสดงตัวอย่างข้อตกลงGetContent()แบบง่าย การแตะปุ่มจะเป็นการเปิดคําขอ ระบบจะเรียกใช้ Lambda ต่อท้ายสำหรับ rememberLauncherForActivityResult() เมื่อผู้ใช้เลือกรูปภาพและกลับไปที่กิจกรรมการเปิดใช้งาน ซึ่งจะโหลดรูปภาพที่เลือกโดยใช้rememberImagePainter() ฟังก์ชันของ Coil

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

การขอสิทธิ์รันไทม์

คุณใช้ Activity Result API และ rememberLauncherForActivityResult() เดียวกันที่อธิบายไว้ด้านบนเพื่อขอสิทธิ์รันไทม์ได้โดยใช้สัญญาสำหรับสิทธิ์เดียวหรือสัญญาสำหรับสิทธิ์หลายรายการก็ได้ โดยสัญญาสำหรับสิทธิ์เดียวจะใช้รูปแบบ RequestPermission ส่วนสัญญาสำหรับสิทธิ์หลายรายการจะใช้รูปแบบ RequestMultiplePermissions

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

การจัดการปุ่มย้อนกลับของระบบ

หากต้องการให้การนําทางกลับที่กําหนดเองและลบล้างลักษณะการทํางานเริ่มต้นของปุ่มย้อนกลับของระบบจากภายในคอมโพสิเบิล คอมโพสิเบิลของคุณสามารถใช้ BackHandler เพื่อขัดขวางเหตุการณ์ดังกล่าว ดังนี้

var backHandlingEnabled by remember { mutableStateOf(true) }
BackHandler(backHandlingEnabled) {
    // Handle back press
}

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

ViewModel

หากใช้ไลบรารี Architecture Components ViewModel คุณจะเข้าถึง ViewModel จากคอมโพสิเบิลใดก็ได้โดยเรียกใช้ฟังก์ชัน viewModel() เพิ่ม Dependency ต่อไปนี้ลงในไฟล์ Gradle

Groovy

dependencies {
    implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.8.5'
}

Kotlin

dependencies {
    implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.5")
}

จากนั้นคุณจะใช้ฟังก์ชัน viewModel() ในโค้ดได้

class MyViewModel : ViewModel() { /*...*/ }

// import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun MyScreen(
    viewModel: MyViewModel = viewModel()
) {
    // use viewModel here
}

viewModel() จะแสดง ViewModel ที่มีอยู่หรือสร้าง ViewModel ใหม่ โดยค่าเริ่มต้น ViewModel ที่แสดงผลจะมีขอบเขตเป็นกิจกรรม ส่วนที่ฝัง หรือปลายทางการนําทางที่กําหนด และระบบจะเก็บไว้ตราบใดที่ขอบเขตยังมีอยู่

ตัวอย่างเช่น หากใช้คอมโพสิเบิลในกิจกรรม viewModel() จะแสดงผลอินสแตนซ์เดียวกันจนกว่ากิจกรรมจะเสร็จสิ้นหรือกระบวนการถูกหยุด

class MyViewModel : ViewModel() { /*...*/ }
// import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun MyScreen(
    // Returns the same instance as long as the activity is alive,
    // just as if you grabbed the instance from an Activity or Fragment
    viewModel: MyViewModel = viewModel()
) { /* ... */ }

@Composable
fun MyScreen2(
    viewModel: MyViewModel = viewModel() // Same instance as in MyScreen
) { /* ... */ }

หลักเกณฑ์การใช้งาน

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

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

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

หาก ViewModel มีการขึ้นต่อกัน viewModel() จะใช้พารามิเตอร์ ViewModelProvider.Factory แบบไม่บังคับ

ดูข้อมูลเพิ่มเติมเกี่ยวกับ ViewModel ใน Compose และวิธีใช้อินสแตนซ์กับไลบรารี Navigation Compose หรือกิจกรรมและฟragment ได้ที่เอกสารการทำงานร่วมกัน

สตรีมข้อมูล

Compose มาพร้อมกับส่วนขยายสำหรับโซลูชันแบบสตรีมที่ได้รับความนิยมสูงสุดของ Android ส่วนขยายแต่ละรายการเหล่านี้มาจากอาร์ติแฟกต์ที่แตกต่างกัน

  • LiveData.observeAsState() รวมอยู่ในอาร์ติแฟกต์ androidx.compose.runtime:runtime-livedata:$composeVersion
  • Flow.collectAsState() ไม่ต้องใช้ทรัพยากรเพิ่มเติม
  • Observable.subscribeAsState() รวมอยู่ในอาร์ติแฟกต์ androidx.compose.runtime:runtime-rxjava2:$composeVersion หรือ androidx.compose.runtime:runtime-rxjava3:$composeVersion

อาร์ติแฟกต์เหล่านี้จะลงทะเบียนเป็นผู้ฟังและแสดงค่าเป็น State เมื่อใดก็ตามที่มีการสร้างค่าใหม่ Compose จะคอมโพสส่วนต่างๆ ของ UI ที่ใช้ state.value นั้นอีกครั้ง ตัวอย่างเช่น ในโค้ดนี้ ShowData จะคอมโพสใหม่ทุกครั้งที่ exampleLiveData ส่งค่าใหม่

// import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun MyScreen(
    viewModel: MyViewModel = viewModel()
) {
    val dataExample = viewModel.exampleLiveData.observeAsState()

    // Because the state is read here,
    // MyScreen recomposes whenever dataExample changes.
    dataExample.value?.let {
        ShowData(dataExample)
    }
}

การดำเนินการแบบไม่พร้อมกันใน Compose

Jetpack Compose ช่วยให้คุณดำเนินการแบบไม่พร้อมกันได้โดยใช้ Coroutine จากภายในคอมโพสิเบิล

ดูข้อมูลเพิ่มเติมเกี่ยวกับ API LaunchedEffect, produceState และ rememberCoroutineScope ได้ในเอกสารประกอบเกี่ยวกับผลข้างเคียง

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

Hilt

Hilt เป็นโซลูชันที่แนะนำสำหรับการฉีดข้อมูลในแอป Android และทำงานร่วมกับ Compose ได้อย่างราบรื่น

ฟังก์ชัน viewModel() ที่กล่าวถึงในส่วน ViewModel จะนํา ViewModel ที่ Hilt สร้างด้วยคําอธิบายประกอบ @HiltViewModel ไปใช้โดยอัตโนมัติ เรามีเอกสารประกอบพร้อมข้อมูลเกี่ยวกับการผสานรวม ViewModel ของ Hilt

@HiltViewModel
class MyViewModel @Inject constructor(
    private val savedStateHandle: SavedStateHandle,
    private val repository: ExampleRepository
) : ViewModel() { /* ... */ }

// import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun MyScreen(
    viewModel: MyViewModel = viewModel()
) { /* ... */ }

Hilt และการนำทาง

Hilt ยังผสานรวมกับไลบรารี Navigation Compose ด้วย เพิ่ม Dependency เพิ่มเติมต่อไปนี้ลงในไฟล์ Gradle

Groovy

dependencies {
    implementation 'androidx.hilt:hilt-navigation-compose:1.2.0'
}

Kotlin

dependencies {
    implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
}

เมื่อใช้ Navigation Compose ให้ใช้ฟังก์ชัน hiltViewModel composable เสมอเพื่อรับอินสแตนซ์ของ @HiltViewModel ที่มีการกำกับเนื้อหา ViewModel ซึ่งใช้ได้กับข้อมูลโค้ดหรือกิจกรรมที่มีคำอธิบายประกอบด้วย @AndroidEntryPoint

ตัวอย่างเช่น หาก ExampleScreen เป็นปลายทางในกราฟการนําทาง ให้เรียกใช้ hiltViewModel() เพื่อรับอินสแตนซ์ของ ExampleViewModel ที่มีขอบเขตเป็นปลายทางดังที่แสดงในข้อมูลโค้ดด้านล่าง

// import androidx.hilt.navigation.compose.hiltViewModel

@Composable
fun MyApp() {
    val navController = rememberNavController()
    val startRoute = "example"
    NavHost(navController, startDestination = startRoute) {
        composable("example") { backStackEntry ->
            // Creates a ViewModel from the current BackStackEntry
            // Available in the androidx.hilt:hilt-navigation-compose artifact
            val viewModel = hiltViewModel<MyViewModel>()
            MyScreen(viewModel)
        }
        /* ... */
    }
}

หากต้องการเรียกข้อมูลอินสแตนซ์ของ ViewModel ที่มีขอบเขตเป็นเส้นทางการนําทางหรือกราฟการนําทางแทน ให้ใช้ฟังก์ชันคอมโพสิเบิล hiltViewModel และส่ง backStackEntry ที่เกี่ยวข้องเป็นพารามิเตอร์ ดังนี้

// import androidx.hilt.navigation.compose.hiltViewModel
// import androidx.navigation.compose.getBackStackEntry

@Composable
fun MyApp() {
    val navController = rememberNavController()
    val startRoute = "example"
    val innerStartRoute = "exampleWithRoute"
    NavHost(navController, startDestination = startRoute) {
        navigation(startDestination = innerStartRoute, route = "Parent") {
            // ...
            composable("exampleWithRoute") { backStackEntry ->
                val parentEntry = remember(backStackEntry) {
                    navController.getBackStackEntry("Parent")
                }
                val parentViewModel = hiltViewModel<ParentViewModel>(parentEntry)
                ExampleWithRouteScreen(parentViewModel)
            }
        }
    }
}

การแบ่งหน้า

ไลบรารีการแบ่งหน้าช่วยให้คุณโหลดข้อมูลทีละน้อยได้ง่ายขึ้น และรองรับใน Compose หน้าการแยกหน้าของรุ่นมีข้อมูลเกี่ยวกับ paging-compose เพิ่มเติมที่ต้องเพิ่มลงในโปรเจ็กต์และเวอร์ชันของโปรเจ็กต์

ต่อไปนี้คือตัวอย่าง Compose API ของไลบรารีการแบ่งหน้า

@Composable
fun MyScreen(flow: Flow<PagingData<String>>) {
    val lazyPagingItems = flow.collectAsLazyPagingItems()
    LazyColumn {
        items(
            lazyPagingItems.itemCount,
            key = lazyPagingItems.itemKey { it }
        ) { index ->
            val item = lazyPagingItems[index]
            Text("Item is $item")
        }
    }
}

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

แผนที่

คุณสามารถใช้ไลบรารี Maps Compose เพื่อให้บริการ Google Maps ในแอปได้ ตัวอย่างการใช้งานมีดังนี้

@Composable
fun MapsExample() {
    val singapore = LatLng(1.35, 103.87)
    val cameraPositionState = rememberCameraPositionState {
        position = CameraPosition.fromLatLngZoom(singapore, 10f)
    }
    GoogleMap(
        modifier = Modifier.fillMaxSize(),
        cameraPositionState = cameraPositionState
    ) {
        Marker(
            state = remember { MarkerState(position = singapore) },
            title = "Singapore",
            snippet = "Marker in Singapore"
        )
    }
}