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

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

กิจกรรม

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

ผลลัพธ์กิจกรรม

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

@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()ฟังก์ชันrememberImagePainter()ของ Coil

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

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

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

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

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

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

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

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

ViewModel

หากใช้ไลบรารี Architecture Components ViewModel คุณจะเข้าถึง ViewModel จาก Composable ใดก็ได้โดย เรียกใช้ฟังก์ชัน 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 ที่ส่งคืนจะมีขอบเขตเป็นกิจกรรม Fragment หรือ ปลายทางการนำทางที่ครอบคลุม และจะยังคงอยู่ตราบใดที่ขอบเขตยังคงใช้งานได้

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

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

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

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

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

สตรีมข้อมูล

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

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

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

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

Hilt

Hilt เป็นโซลูชันที่แนะนำสำหรับการแทรกทรัพยากร Dependency ในแอป 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 annotated ViewModel ซึ่งใช้ได้กับ Fragment หรือ Activity ที่มีคำอธิบายประกอบด้วย @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 มีข้อมูลเกี่ยวกับpaging-composeการขึ้นต่อกันเพิ่มเติมที่ต้องเพิ่ม ลงในโปรเจ็กต์และเวอร์ชัน

ตัวอย่าง API ของ Compose ใน Paging Library มีดังนี้

@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"
        )
    }
}