たくさんの自由帳
Androidのお話
たくさんの自由帳
文字数(だいたい) : 1588
あけましておめでとうございます!!!
年明けはM.2 SSDを換装してWindows 11にアップデートしてました。起動音がなるんだ。
CreationExtrasにApplicationがないエラーだったので、付け焼き刃ですがApplicationを渡せば解決。
知らないうちにLocalActivityなるものが生えてる
SubScreen(
viewModel = viewModel(
extras = MutableCreationExtras(
(LocalViewModelStoreOwner.current as? HasDefaultViewModelProviderFactory)?.defaultViewModelCreationExtras ?: CreationExtras.Empty
).apply {
// Application を渡す付け焼き刃
set(ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY, LocalActivity.current!!.application)
}
),
title = "ホーム画面"
)Navigation3と遊んでました。
感想はまたあとで!そんなことより!
java.lang.IllegalArgumentException: CreationExtras must have an application by `APPLICATION_KEY`Navigation3でも同じようにViewModelをサポートしていますが、ついに、AndroidViewModelが切られてしまった。
もともとViewModelでContextを直で使うべきではないってAndroidが公式で言ってたので・・・
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
AndroidNavigation3AndroidViewModelTheme {
MainScreen()
}
}
}
}
class HomeViewModel(application: Application) : AndroidViewModel(application) // Android の ViewModel がエラーに...
@Composable
private fun MainScreen() {
val backStack = rememberNavBackStack(Links.Home)
NavDisplay(
backStack = backStack,
entryDecorators = listOf(
rememberSaveableStateHolderNavEntryDecorator(),
rememberViewModelStoreNavEntryDecorator()
),
entryProvider = entryProvider {
entry<Links.Home> { link ->
SubScreen(
viewModel = viewModel(),
title = "ホーム画面"
)
}
}
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun SubScreen(
viewModel: HomeViewModel,
title: String
) {
Scaffold(
topBar = { TopAppBar(title = { Text(title) }) }
) { innerPadding ->
// do nothing
}
}
sealed interface Links : NavKey {
@Serializable
data object Home : Links
}Android Studio Otter 2 Feature Drop | 2025.2.2 Patch 1
[versions]
# 省略...
nav3Core = "1.0.0"
lifecycleViewmodelNav3 = "2.10.0"
[libraries]
# 省略...
androidx-navigation3-runtime = { module = "androidx.navigation3:navigation3-runtime", version.ref = "nav3Core" }
androidx-navigation3-ui = { module = "androidx.navigation3:navigation3-ui", version.ref = "nav3Core" }
androidx-lifecycle-viewmodel-navigation3 = { module = "androidx.lifecycle:lifecycle-viewmodel-navigation3", version.ref = "lifecycleViewmodelNav3" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerialization" }
[plugins]
# 省略...
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }冒頭の通りです。Issueを見る限りApplicationがなくなったのは期待通りらしい。
あと、追加で引数を渡したい場合はfactory =の引数を埋めればよいです。
class HomeViewModel(application: Application, links: Links) : AndroidViewModel(application) // 画面遷移の data class も欲しいSubScreen(
viewModel = viewModel(
extras = MutableCreationExtras(
(LocalViewModelStoreOwner.current as? HasDefaultViewModelProviderFactory)?.defaultViewModelCreationExtras ?: CreationExtras.Empty
).apply {
// Application を渡す付け焼き刃
set(ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY, LocalActivity.current!!.application)
},
factory = viewModelFactory {
initializer {
HomeViewModel(
application = this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY]!!,
links = it // 引数がある場合
)
}
}
),
title = "ホーム画面"
)おわり。
いる?
まだあんまり使えてないけど。真髄はSceneとかなんだと思う。
Navigation2であったCompose Navigationとの違いは、バックスタック(画面遷移履歴)をつかさどるrememberNavBackStack()が、MutableListを実装している点でしょうか。
画面遷移はMutableListを自分で操作することによって行うようになりました。add()すれば画面が切り替わり、removeLastOrNull()すれば画面が閉じられる。
Navigation2の時は今表示されてる画面を取得するのに、どっかのクラスにあるFlowをcollectAsState()したような気がしますが、
今作ではただの配列になったので、MutableListの最後の要素を取るだけで済むはず。
あと配列を操作すればよくなったので、前の画面に戻れない挙動がすぐできるのが強い。
これが一番のメリットかもしれない。popUpTo()とinclusiveをいい感じにすればいいんだろうけどあのAPIわかりにくすぎる。
entry<NavigationLinkList.PermissionScreen> {
PermissionScreen(
onGranted = {
// 履歴配列から消してホームを出す
backStack += NavigationLinkList.HomeScreen
backStack -= NavigationLinkList.PermissionScreen
}
)
}
entry<NavigationLinkList.HomeScreen> {
HomeScreen(
onNavigate = { dest -> backStack += dest }
)
}きになる点としては
composable("${NavigationPaths.VideoEditor.path}/{projectName}?openVideoInfo={openVideoInfo}") {
VideoEditorScreen(
onNavigate = { navigationPaths -> navController.navigate(navigationPaths.path) },
onBack = { navController.popBackStack() }
)
}class VideoEditorViewModel(
private val application: Application,
private val savedStateHandle: SavedStateHandle
) : AndroidViewModel(application) {
// SavedStateHandle 経由で
val projectName: String = savedStateHandle["projectName"]!!
}画面遷移のパラメーターをSavedStateHandleに自動的に入れてくれる機能がありましたが、この機能は無くなったようです。
今のところviewModelFactory { }で引数付きViewModelを作れるようにする案と、KoinとかのDIライブラリだともっと簡単に渡せるっぽいです。DIライブラリに入門しておくんだった・・・
あとは文字列のパスからdata classとかを使ったパスになった。パラメーターが型安全の一方、kotlinx.serializationがルーティングに必要な時代かと思った。
たぶんserializationもdata classも使わずenumでも動きそうな気はしますが、公式の方法から逸脱してまでする気も起きないので。