Compose và các thư viện khác

Bạn có thể sử dụng các thư viện yêu thích trong Compose. Mục này mô tả cách kết hợp một số thư viện hữu ích nhất.

Hoạt động

Để sử dụng Compose trong một hoạt động, bạn phải sử dụng ComponentActivity, một lớp con của Activity cung cấp LifecycleOwner thích hợp và các thành phần cho Compose. Lớp con này cũng cung cấp các API bổ sung giúp tách mã khỏi các phương thức ghi đè trong lớp hoạt động của bạn. Activity Compose hiển thị những API này với các thành phần kết hợp sao cho không cần phải ghi đè những phương thức diễn ra bên ngoài các thành phần kết hợp hoặc truy xuất một thực thể Activity rõ ràng. Thêm vào đó, các API này đảm bảo chúng chỉ được khởi chạy một lần, duy trì quá trình tái cấu trúc, đồng thời dọn dẹp đúng cách nếu thành phần kết hợp bị xoá khỏi cấu trúc.

Kết quả hoạt động

API rememberLauncherForActivityResult() cho phép bạn nhận kết quả từ một hoạt động trong thành phần kết hợp của bạn:

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

Ví dụ này minh hoạ một hợp đồng GetContent() đơn giản. Nhấn vào nút để khởi chạy yêu cầu. Cú pháp trailing Lambda cho rememberLauncherForActivityResult() được dẫn ra khi người dùng chọn một hình ảnh và quay lại hoạt động khởi chạy. Thao tác này tải hình ảnh đã chọn bằng hàm rememberImagePainter() của Coil.

Bạn có thể sử dụng bất kỳ lớp con nào củaActivityResultContract làm đối số đầu tiên cho rememberLauncherForActivityResult(). Tức là bạn có thể sử dụng kỹ thuật này để yêu cầu nội dung từ khung và các mẫu phổ biến khác. Bạn cũng có thể tạo các hợp đồng tuỳ chỉnh của riêng mình và sử dụng chúng bằng kỹ thuật này.

Yêu cầu quyền truy cập Runtime

Bạn có thể dùng cùng một Activity Result API (API Kết quả hoạt động) và rememberLauncherForActivityResult() đã trình bày ở trên để yêu cầu cấp quyền khi bắt đầu chạy bằng cách sử dụng hợp đồng RequestPermission để được cấp một quyền hoặc sử dụng hợp đồng RequestMultiplePermissions để được cấp nhiều quyền.

Thư viện Quyền truy cập nhóm thư viện cũng có thể sử dụng một lớp phía trên các API đó để liên kết trạng thái hiện tại đã cấp cho các quyền truy cập vào Trạng thái mà công cụ Compose có thể sử dụng.

Xử lý nút quay lại hệ thống

Để cung cấp hoạt động điều hướng quay lại tuỳ chỉnh và ghi đè chế độ mặc định của nút quay lại hệ thống từ bên trong thành phần kết hợp của bạn, thành phần kết hợp này có thể sử dụng hàm BackHandler để can thiệp vào sự kiện đó:

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

Đối số đầu tiên kiểm soát mã BackHandler có đang được bật hay không; bạn có thể sử dụng đối số này để tạm thời tắt trình xử lý dựa trên trạng thái của thành phần. Biểu thức lambda tạo vệt sẽ được gọi nếu người dùng kích hoạt sự kiện quay lại hệ thống và BackHandler hiện đang bật.

ViewModel

Nếu sử dụng thư viện Thành phần cấu trúc ViewModel, bạn có thể truy cập vào ViewModel từ bất kỳ thành phần kết hợp nào bằng cách gọi hàm viewModel().

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

@Composable
fun MyScreen(
    viewModel: MyViewModel = viewModel()
) {
    // use viewModel here
}

viewModel()trả về một chế độ ViewModel hiện có hoặc tạo một chế độ mới trong phạm vi nhất định. ViewModel được giữ lại miễn là phạm vi vẫn hoạt động. Ví dụ: nếu sử dụng thành phần kết hợp trong một hoạt động, viewModel() sẽ trả về cùng một phiên bản cho đến khi hoạt động đó kết thúc hoặc quá trình kết thúc.

@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
) { /* ... */ }

Nếu ViewMode của bạn có các phần phụ thuộc, viewModel() sẽ lấy ViewModelProvider.Factory tùy ý làm tham số.

Để biết thêm thông tin về ViewModel trong Compose và cách sử dụng các phiên bản với thư viện Compose Navigation, hoặc các hoạt động và mảnh, vui lòng xem tài liệu về Khả năng tương tác.

Trình phát dữ liệu trực tuyến

Compose đi kèm với phần mở rộng cho các giải pháp dựa trên chế độ phát trực tuyến phổ biến nhất của Android. Mỗi phần mở rộng trong số này được một cấu phần phần mềm khác nhau cung cấp:

  • LiveData.observeAsState() có trong cấu phần phần mềm androidx.compose.runtime:runtime-livedata:$composeVersion.
  • Flow.collectAsState() không yêu cầu thêm phần phụ thuộc.
  • Observable.subscribeAsState() có trong cấu phần phần mềm androidx.compose.runtime:runtime-rxjava2:$composeVersion hoặc androidx.compose.runtime:runtime-rxjava3:$composeVersion.

Các cấu phần phần mềm này đăng ký dưới dạng trình nghe và thể hiện các giá trị dưới dạng một State. Bất cứ khi nào có giá trị mới được đưa ra, công cụ Compose sẽ kết hợp lại các phần đó của giao diện người dùng nơi sử dụng state.value. Ví dụ: trong mã này, ShowData sẽ kết hợp lại mỗi khi exampleLiveData đưa ra một giá trị mới.

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

Thao tác không đồng bộ trong công cụ Compose

Jetpack Compose cho phép bạn thực thi các thao tác không đồng bộ bằng cách sử dụng coroutine từ trong những thành phần kết hợp của bạn.

Hãy xem các API LaunchedEffect, produceStaterememberCoroutineScope trong tài liệu về hiệu ứng lề để biết thêm thông tin.

Bạn nên sử dụng thư viện Navigation Compose để thêm các thành phần điều hướng vào dự án Compose của mình. Các thành phần này cho phép bạn thêm giao diện người dùng để di chuyển giữa những thành phần kết hợp trong khi vẫn tận dụng được cấu trúc và tính năng của thành phần Điều hướng.

Hãy tìm hiểu thêm về hoạt động tích hợp này trong tài liệu Điều hướng với Compose.

Hilt

Hilt là giải pháp được đề xuất để chèn phần phụ thuộc vào các ứng dụng Android, giải pháp này hoạt động liền mạch với Compose.

Hàm viewModel() được đề cập trong Mục ViewModel sẽ tự động sử dụng ViewModel mà Hilt tạo ra với chú thích @HiltViewModel. Chúng tôi đã cung cấp cho bạn tài liệu với thông tin về Tích hợp ViewModel của Hilt.

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

@Composable
fun MyScreen(
    viewModel: MyViewModel = viewModel()
) { /* ... */ }

Hilt và điều hướng

Hilt cũng tích hợp với thư viện Navigation Compose. Thêm các phần phụ thuộc bổ sung sau đây vào tệp Gradle của bạn:

Groovy

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

Kotlin

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

Khi sử dụng tính năng Navigation Compose, hãy luôn sử dụng hàm kết hợp hiltViewModel để có được bản sao của @HiltViewModel có chú thích ViewModel. Tính năng này hoạt động với các mảnh hoặc hoạt động được chú giải bằng @AndroidEntryPoint.

Ví dụ: nếu ExampleScreen là một đích đến trong một biểu đồ điều hướng, hãy gọi hiltViewModel() để lấy thực thể của ExampleViewModel trong phạm vi đích đến được hiển thị trong đoạn mã dưới đây:

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

@Composable
fun MyApp() {
    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)
        }
        /* ... */
    }
}

Nếu bạn cần truy xuất bản sao của ViewModel nằm trong phạm vi tuyến đường điều hướng hoặc sơ đồ điều hướng thay thế, sử dụng hàm có khả năng kết hợp hiltViewModel và truyền tham số backStackEntry tương ứng:

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

@Composable
fun MyApp() {
    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)
            }
        }
    }
}

Paging

Thư viện Paging giúp bạn tải dữ liệu dễ dàng hơn và tính năng này được hỗ trợ trong công cụ Compose. Trang phát hành Paging có chứa thông tin về phần phụ thuộc paging-compose bổ sung cần được thêm vào dự án và phiên bản của dự án đó.

Dưới đây là một ví dụ về các API Compose của thư viện Paging:

@Composable
fun MyScreen(flow: Flow<PagingData<String>>) {
    val lazyPagingItems = flow.collectAsLazyPagingItems()
    LazyColumn {
        items(lazyPagingItems) {
            Text("Item is $it")
        }
    }
}

Hãy xem Tài liệu về Danh sách và lưới để biết thêm thông tin về cách sử dụng tính năng Phân trang trong Compose.

Maps

Bạn có thể sử dụng thư viện Maps Compose để tích hợp Google Maps vào ứng dụng của mình. Sau đây là ví dụ về cách sử dụng:

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