Navigation Components を使ってみる

書いてあるがわかりにくい定期

これを読んで良さそうと思った

辞書的に見るのに良さそうな

dependencies {
def nav_version = "2.3.3"
// Java language implementation
implementation "androidx.navigation:navigation-fragment:$nav_version"
implementation "androidx.navigation:navigation-ui:$nav_version"
// Kotlin
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
// Feature module Support
implementation "androidx.navigation:navigation-dynamic-features-fragment:$nav_version"
// Testing Navigation
androidTestImplementation "androidx.navigation:navigation-testing:$nav_version"
// Jetpack Compose Integration
implementation "androidx.navigation:navigation-compose:1.0.0-alpha07"
}
これは全部書かないとだめなんだろうか

dependencies {
def nav_version = "【任意のバージョン】"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
}
まずこれだけ

fragmentTransaction
とか intent
とか使ってたところから同移行するのかわからぬ

ここクリックしたら、既存のが選択肢にでてきた

遷移の矢印が出るらしいが出し方がわからない

矢印クリックしたところからdestination指定で追加できた

activity -> activity はこれではあんまり扱わんのかな

設定した時の挙動がよくわからぬ

fragment の遷移を定義しておいて、それを root の activity で呼び出して、セットすると、fragment の遷移ができるようになる、みたいなことだろうか

初期 fragment はどのタイミングで入るのか
空っぽのフラグメントに
private fun replaceFragment(fragment: Fragment) {
val fragmentManager = supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
fragmentTransaction.replace(R.id.fragment_container, fragment)
fragmentTransaction.commit()
}
こんな感じで入れていたところ

setupActionBarWithNavController でセットされるのか、その後になんか呼ぶ必要があるのか

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
val navController = findNavController(R.id.main_nav_host)
val appBarConfiguration = AppBarConfiguration(navController.graph)
setupActionBarWithNavController(navController, appBarConfiguration)
}
override fun onSupportNavigateUp()
= findNavController(R.id.main_nav_host).navigateUp()
}
ここ書いたけど
java.lang.RuntimeException: Unable to start activity ComponentInfo{}: java.lang.IllegalArgumentException: ID does not reference a View inside this Activity
で落ちる

画面サイズごとに呼び出し先の activity を変えてるので、同じ名前のが複数あって、全部に適用できてなかった

val navController = findNavController(R.id.top_up_nav_host)
val appBarConfiguration = AppBarConfiguration(navController.graph)
setupActionBarWithNavController(navController, appBarConfiguration)
これで does not have a NavController set on
みたいなエラーが出た

val navController = supportFragmentManager.findFragmentById(R.id.top_up_nav_host) as NavHostFragment
setupActionBarWithNavController(navController.navController)
これもだめ
java.lang.RuntimeException: Unable to start activity ComponentInfo{}: java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.appcompat.app.ActionBar.setTitle(java.lang.CharSequence)' on a null object reference

val navController = supportFragmentManager.findFragmentById(R.id.top_up_nav_host) as NavHostFragment
setupActionBarWithNavController(navController.navController)
これもだめ
java.lang.RuntimeException: Unable to start activity ComponentInfo{}: java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.appcompat.app.ActionBar.setTitle(java.lang.CharSequence)' on a null object reference

github で探してみたけど良いのがでてこない

これ良さそうな気配

class MainFragment() : ContainerBaseFragment(R.layout.fragment_main) {
private lateinit var binding: FragmentTopBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentMainBinding.inflate(inflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.nextButton.setOnClickListener {
findNavController().navigate(R.id.{action_id})
}
}
}
これだけでいけた
findNavController().navigate(R.id.{action_id})
これかいただけ
fragment に書かないといけなかった?
{action_id} は、navigation.xml で定義したものの action の id

最小構成で動いたので、Safe Args をいれてみる

class MainFragment() : ContainerBaseFragment(R.layout.fragment_main) {
private lateinit var binding: FragmentTopBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentMainBinding.inflate(inflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.nextButton.setOnClickListener {
findNavController().navigate(MainFragmentDirections.actionMainFragmentToSecondFragment())
}
}
}
こんな感じで動くようになった

あとは fragment 間で値渡しがやりたい

findNavController().navigate({Fragment名}Directions.action())
Fragment名が自クラスと異なっていると、遷移発火時にエラーで落ちる
java.lang.IllegalArgumentException: Navigation action/destination
(ビルド時に検出できないものなのか)

<action
android:id="@+id/action_mainFragment_to_secondFragment"
app:destination="@id/secondFragment"
app:popUpToInclusive="false" />
app:popUpToInclusive="false"
にしても backstack 積まれてる
どうして

用語 | 説明 |
---|---|
Pop To |
Back ボタンを押したとき、どこの画面まで戻るか指定できる |
Inclusive |
Pop Up で指定した画面の1つ前に戻るようにする |
app:popUpToInclusive="false"
これは backstack を積まないとかそういうことではないっぽい
app:popUpTo={Root}
これすると、アプリ終了まで戻る

app:popUpTo="@id/MainFragment"
で複数遷移した後のどの位置にいても back すると Main までもどる
他の指定方法がありそうな気もするけど

一旦できたのでおわり

activity から activity 移動したい

activity の fragment -> activity はできても
activity 単体 -> activity はできなさそう