Compose でお好みのライブラリを使用できます。このセクションでは、有用なライブラリを組み込む方法について説明します。
アクティビティ
アクティビティで Compose を使用するには、ComponentActivity
を使用する必要があります。これは、Compose に適切な LifecycleOwner
とコンポーネントを提供する Activity
のサブクラスです。また、アクティビティ クラスのメソッドのオーバーライドからコードを分離する追加の 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()
の最初の引数として使用できます。つまり、この方法を使用して、フレームワークや他の一般的なパターンでコンテンツをリクエストできます。また、独自のカスタム コントラクトを作成して、この方法で使用することもできます。
実行時の権限のリクエスト
前述の Activity Result API と rememberLauncherForActivityResult()
は、1 つの権限に対する RequestPermission
コントラクト、または複数の権限に対する RequestMultiplePermissions
コントラクトを使って実行時の権限のリクエストに使用できます。
これらの API の上のレイヤに Accompanist Permissions ライブラリを使用して、権限の現在の付与された状態を、Compose UI で使用できる State にマッピングすることもできます。
システムの [戻る] ボタンの処理
コンポーザブル内からカスタムの「戻る」ナビゲーションを提供し、システムの [戻る] ボタンのデフォルトの動作をオーバーライドするには、コンポーザブルで 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.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()
は、アクティビティが終了するまで、またはプロセスが強制終了されるまで、同じインスタンスを返します。
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
インスタンスには、画面レベルのコンポーザブルでアクセスします。これは、アクティビティ、フラグメント、または Navigation グラフのデスティネーションから呼び出されるルート コンポーザブルに近いものを指します。これは、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 部分を再コンポーズします。たとえば、このコードでは、exampleLiveData
が新しい値を出力するたびに ShowData
が再コンポーズされます。
// 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
API、produceState
API、rememberCoroutineScope
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 と Navigation
Hilt も Navigation Compose ライブラリと統合されています。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
コンポーズ可能な関数を使って、@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 ライブラリを使用すると、データを段階的に読み込むことが容易になります。これは 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 = remember { MarkerState(position = singapore) }, title = "Singapore", snippet = "Marker in Singapore" ) } }
あなたへのおすすめ
- 注: JavaScript がオフになっている場合はリンクテキストが表示されます
- Compose における副作用
- 状態と Jetpack Compose
- Compose で UI 状態を保存する