Compose 및 기타 라이브러리

Compose에서는 자주 이용하는 라이브러리를 사용할 수 있습니다. 이 섹션에서는 몇 가지 가장 유용한 라이브러리를 통합하는 방법을 설명합니다.

활동

활동에서 Compose를 사용하려면 적절한 LifecycleOwner와 구성요소를 Compose에 제공하는 Activity의 서브클래스인 ComponentActivity를 사용해야 합니다. 이 서브클래스는 활동 클래스의 메서드 재정의에서 코드를 분리하는 추가 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() 계약을 보여줍니다. 버튼을 탭하면 요청이 실행됩니다. rememberLauncherForActivityResult()의 후행 람다는 사용자가 이미지를 선택하고 실행 활동으로 돌아가면 호출됩니다. 그러면 Coil의 rememberImagePainter() 함수를 통해 선택한 이미지가 로드됩니다.

ActivityResultContract의 서브클래스는 rememberLauncherForActivityResult()의 첫 번째 인수로 사용할 수 있습니다. 즉, 이 기법을 사용하여 프레임워크와 다른 일반 패턴에서 콘텐츠를 요청할 수 있습니다. 또한 맞춤 계약을 자체적으로 만들어 이 기법과 함께 사용할 수도 있습니다.

런타임 권한 요청

단일 권한에 RequestPermission 계약을 사용하거나 여러 권한에 RequestMultiplePermissions 계약을 사용하여 런타임 권한을 요청할 때, 위에서 설명한 동일한 Activity Result API와 rememberLauncherForActivityResult()를 사용할 수 있습니다.

Accompanist 권한 라이브러리 해당 API 위에 있는 레이어를 사용하여 현재 부여된 상태를 권한을 Compose UI에서 사용할 수 있는 상태로 전환합니다.

시스템 뒤로 버튼 처리

맞춤 뒤로 탐색을 제공하고 컴포저블 내에서 시스템 뒤로 버튼의 기본 동작을 재정의하려면 컴포저블이 BackHandler를 사용하여 관련 이벤트를 가로채면 됩니다.

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

첫 번째 인수는 BackHandler의 현재 사용 설정 여부를 제어합니다. 이 인수를 사용하여 구성요소 상태에 따라 핸들러를 일시적으로 중지할 수 있습니다. 사용자가 시스템 뒤로 이벤트를 트리거하고 BackHandler가 현재 사용 설정된 경우 후행 람다가 호출됩니다.

ViewModel

아키텍처 구성요소 ViewModel 라이브러리를 사용하는 경우 viewModel() 함수를 호출하여 컴포저블에서 ViewModel에 액세스할 수 있습니다. Gradle 파일에 다음 종속 항목을 추가합니다.

Groovy

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

Kotlin

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

그런 다음 코드에서 viewModel() 함수를 사용할 수 있습니다.

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

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

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 인스턴스에 액세스합니다. 컴포저블, 즉 활동에서 호출되는 루트 컴포저블에 가까우며 프래그먼트 또는 탐색 그래프의 대상입니다. 이는 ViewModel에서 기본적으로 화면 수준 객체로 범위가 지정됩니다. 에 대해 자세히 알아보기 ViewModel 여기에서 수명 주기와 범위를 확인하세요.

지나치게 많은 내용을 전달하는 데 필요한 ViewModel 인스턴스를 다른 컴포저블에 할당. 이러한 컴포저블이 테스트하기가 더 어려우며 미리보기 대신 매개변수로 전달해야 합니다.

ViewModel 인스턴스를 사용하여 다음을 할 수 있습니다. 하위 화면 수준 컴포저블의 상태를 관리할 수 있지만 ViewModel수명 주기 및 범위를 참조하세요. 만약 컴포저블이 독립 실행형이므로 Hilt를 사용하여 상위 요소로부터 종속 항목을 전달할 필요가 없도록 ViewModel를 삽입합니다. 구성 가능한 함수입니다.

ViewModel에 종속 항목이 있는 경우 viewModel()는 선택사항을 사용합니다. ViewModelProvider.Factory 를 매개변수로 사용합니다.

Compose의 ViewModel, 인스턴스가 Navigation Compose 라이브러리와 함께 사용되는 방식 또는 활동 및 프래그먼트에 관한 자세한 내용은 상호 운용성 문서를 참고하세요.

데이터 스트림

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는 state.value가 사용되는 UI의 해당 부분을 재구성합니다. 예를 들어 다음 코드에서 ShowDataexampleLiveData가 새 값을 내보낼 때마다 재구성됩니다.

// 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를 사용하면 컴포저블 내에서 코루틴을 사용하여 비동기 작업을 실행할 수 있습니다.

자세한 내용은 부수 효과 문서에서 LaunchedEffect, produceStaterememberCoroutineScope API를 참고하세요.

Navigation 구성요소는 Jetpack Compose 애플리케이션을 지원합니다. Compose를 통해 이동 및 자세한 내용은 Jetpack Navigation을 Navigation Compose로 이전하세요.

Hilt

Hilt는 Android 앱에서 종속 항목 삽입을 위해 권장되는 솔루션이며 Compose와 함께 원활하게 작동합니다.

ViewModel 섹션에서 언급된 viewModel() 함수는 Hilt가 @HiltViewModel 주석을 사용하여 구성하는 ViewModel을 자동으로 사용합니다. 자세한 내용은 Hilt의 ViewModel 통합에 관한 문서를 참고하세요.

@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 라이브러리와도 통합됩니다. Gradle 파일에 다음과 같은 종속 항목을 추가합니다.

Groovy

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

Kotlin

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

Navigation Compose를 사용할 때는 항상 구성 가능한 hiltViewModel 함수를 사용하여 @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)
            }
        }
    }
}

Paging

Paging 라이브러리를 사용하면 데이터를 점진적이고 매끄럽게 로드할 수 있으며 Paging 라이브러리는 Compose에서 지원됩니다. Paging 출시 페이지에는 프로젝트에 추가해야 하는 추가 paging-compose 종속 항목 및 버전에 관한 정보가 포함되어 있습니다.

다음은 Paging 라이브러리의 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에서 Paging을 사용하는 것에 관한 자세한 내용은 목록 및 그리드 문서를 참고하세요.

지도

Maps Compose 라이브러리를 사용하여 앱에서 Google 지도를 제공할 수 있습니다. 사용 예는 다음과 같습니다.

@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 = MarkerState(position = singapore),
            title = "Singapore",
            snippet = "Marker in Singapore"
        )
    }
}