Compose とその他のライブラリ

Compose でお好みのライブラリを使用できます。このセクションでは、有用なライブラリを組み込む方法について説明します。

ViewModel

アーキテクチャ コンポーネントの ViewModel ライブラリを使用している場合、viewModel() 関数を呼び出すことで、任意のコンポーザブルから ViewModel にアクセスできます。

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

@Composable
fun MyExample(
    viewModel: ExampleViewModel = viewModel()
) {
    // use viewModel here
}

viewModel() は、既存の ViewModel を返すか、指定されたスコープで新しく作成します。ViewModel は、スコープが存続している限り保持されます。たとえば、コンポーザブルがアクティビティで使用されている場合、viewModel() は、アクティビティが終了するまで、またはプロセスが強制終了されるまで、同じインスタンスを返します。

@Composable
fun MyExample(
    // Returns the same instance as long as the activity is alive,
    // just as if you grabbed the instance from an Activity or Fragment
    viewModel: ExampleViewModel = viewModel()
) { /* ... */ }

@Composable
fun MyExample2(
    viewModel: ExampleViewModel = viewModel() // Same instance as in MyExample
) { /* ... */ }

ViewModel に依存関係がある場合、viewModel() はオプションの ViewModelProvider.Factory をパラメータとして受け取ります。

Compose の ViewModel と、Compose Navigation ライブラリまたはアクティビティとフラグメントでインスタンスを使用する方法については、相互運用性のドキュメントをご覧ください。

データのストリーム

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 部分を再コンポーズします。たとえば、このコードでは、exampleLiveData が新しい値を出力するたびに ShowData が再コンポーズされます。

@Composable
fun MyExample(
    viewModel: ExampleViewModel = viewModel()
) {
    val dataExample = viewModel.exampleLiveData.observeAsState()

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

Compose の非同期オペレーション

Jetpack Compose では、コンポーザブル内からコルーチンを使用して非同期オペレーションを実行できます。

詳しくは、副作用に関するドキュメントLaunchedEffect API、produceState API、rememberCoroutineScope API をご覧ください。

Compose Navigation ライブラリを使用して、Compose プロジェクトにナビゲーション要素を追加することをおすすめします。それらの要素を使用すると、Navigation コンポーネントのインフラストラクチャと機能を利用して、コンポーザブル間をナビゲートする UI を追加できます。

この統合について詳しくは、Compose を使用したナビゲーションのドキュメントをご覧ください。

Hilt

Hilt は、Android アプリで依存性を注入するための推奨されるソリューションであり、Compose とシームレスに連携します。

ViewModel セクションに記載されている viewModel() 関数は、Hilt が @HiltViewModel アノテーションを使用して構築する ViewModel を自動的に使用します。Hilt の ViewModel 統合に関するドキュメントが提供されています。

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

@Composable
fun ExampleScreen(
    exampleViewModel: ExampleViewModel = viewModel()
) { /* ... */ }

Hilt と Navigation

Hilt は、Compose Navigation ライブラリとも統合されています。Gradle ファイルに次の依存関係を追加します。

app/build.gradle

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

@HiltViewModel のアノテーションが付けられた ViewModelスコープがナビゲーション グラフに設定されている場合は、@AndroidEntryPoint のアノテーションが付けられたフラグメントまたはアクティビティで機能するコンポーズ可能な関数 hiltViewModel を使用します。

たとえば、ExampleScreen がナビゲーション グラフのデスティネーションである場合は、下記のコード スニペットに示されているように、hiltViewModel() を呼び出して、デスティネーションにスコープを設定された ExampleViewModel のインスタンスを取得します。

// 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 exampleViewModel = hiltViewModel<ExampleViewModel>()
            ExampleScreen(exampleViewModel)
        }
        /* ... */
    }
}

代わりに、ナビゲーション ルートにスコープを設定された ViewModel のインスタンスを取得する必要がある場合は、デスティネーション ルートをパラメータとして渡します。

// 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 parentViewModel = hiltViewModel<ParentViewModel>(
                    navController.getBackStackEntry("Parent")
                )
                ExampleWithRouteScreen(parentViewModel)
            }
        }
    }
}

Paging

Paging ライブラリを使用すると、データを段階的に読み込むことが容易になります。これは Compose でサポートされています。Paging リリースのページには、プロジェクトとそのバージョンに追加する必要がある特別な paging-compose 依存関係の情報が記載されています。

Paging ライブラリの Compose API の例を次に示します。

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

Compose で Paging を使用する方法について詳しくは、リストのドキュメントをご覧ください。

画像の読み込み

Instacart の Coil ライブラリには、ネットワーク経由でリモート画像を読み込むなど、外部ソースから画像を読み込むためのコンポーズ可能な関数が用意されています。

io.coil-kt:coil-compose アーティファクトからの rememberImagePainter の例を次に示します。

@Composable
fun MyExample() {
    val painter = rememberImagePainter(
        data = "https://picsum.photos/300/300",
        builder = {
            crossfade(true)
        }
    )

    Box {
        Image(
            painter = painter,
            contentDescription = stringResource(R.string.image_content_desc),
        )

        when (painter.state) {
            is ImagePainter.State.Loading -> {
                // Display a circular progress indicator whilst loading
                CircularProgressIndicator(Modifier.align(Alignment.Center))
            }
            is ImagePainter.State.Error -> {
                // If you wish to display some content if the request fails
            }
        }
    }
}