6
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Navigationを実際に使ってみた時のまとめ

Last updated at Posted at 2019-07-27

Navigationとは?

公式ページ

画面遷移に関する事をいい感じに実装できる優れもの
以下の3つのコンポーネントを理解する事が重要 :sparkles:

  • Navigation graph
    • 画面遷移を1つのXMLファイルで集中的に管理
  • NavHost
    • ナビゲーショングラフから目的地を表示する空のコンテナ
  • NavController
    • ナビゲーション管理

Navigationを使う事の利点

  • Fragmentトランザクションの管理
  • Up, Backイベント処理
  • アニメーションとトランジションのための標準化されたリソースを提供
  • DeepLinkの処理
  • 最小限の追加作業で、ナビゲーションパネルや下部ナビゲーションなどのナビゲーションUIパターンを含めることができる
  • Safe Args
  • ViewModelのサポート

Navigation Editor

AndroidStudio 3.3以上の環境でナビゲーションをGUIで操作できる

:computer:環境構築


新しく NavigationSample としてプロジェクトを作成し
dependenciesはNavigationとSafeArgsをインストールします。

rootのbuild.gradle

    repositories {
        google()
    }
    dependencies {
        // SafeArgs
        def nav_version = "2.1.0-alpha06"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
    }

moduleの build.gradle

apply plugin: "androidx.navigation.safeargs.kotlin"

dependencies {
    // Navigation
    def navi_version = "2.1.0-alpha06"
    implementation "androidx.navigation:navigation-fragment-ktx:$navi_version"
    implementation "androidx.navigation:navigation-ui-ktx:$navi_version"
}

公式ドキュメント

:pencil: 実装


Navigation Graph の作成

AndroidStudioでリソース作成時にリソースタイプを Navigation に設定し、
ファイル名を入力して作成
ファイル名はGet started通りに nav_graph.xml で作成

nav1.png

2つのフラグメントの作成

  • MainFragment
class MainFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_main, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        buttonToSecond.setOnClickListener {
            Navigation.findNavController(it).navigate(R.id.action_mainFragment_to_secondFragment)
        }
    }
}
  • SecondFragment
class SecondFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_second, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        buttonToMain.setOnClickListener {
            Navigation.findNavController(it).navigate(R.id.action_secondFragment_to_mainFragment)
        }
    }
}

※ Navigation.findNavController... は以下の様にも書ける

buttonToSub.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_xxx, null))

Navigation Editor での設定

「New Destination」をクリックすると既に存在しているFragmentやActivityが
追加できる。またこの画面から新規にFragment等を作成もできる。

nav2.png

そして上記の2つのフラグメントを繋ぐように設定した navigation graphがこちら

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            xmlns:tools="http://schemas.android.com/tools" android:id="@+id/nav_graph"
            app:startDestination="@id/mainFragment">

    <fragment android:id="@+id/mainFragment" android:name="slowhand.com.navigationsample.MainFragment"
              android:label="fragment_main" tools:layout="@layout/fragment_main">
        <action android:id="@+id/action_mainFragment_to_secondFragment" app:destination="@id/secondFragment"/>
    </fragment>
    <fragment android:id="@+id/secondFragment" android:name="slowhand.com.navigationsample.SecondFragment"
              android:label="fragment_second" tools:layout="@layout/fragment_second">
        <action android:id="@+id/action_secondFragment_to_mainFragment" app:destination="@id/mainFragment"/>
    </fragment>
</navigation>

このようになりました。

NavHostFragmentの指定

MainActivityのレイアウトファイルで、NavHostFragmentを追加しています。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

    <fragment
            android:id="@+id/nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:defaultNavHost="true"
            app:navGraph="@navigation/nav_graph"/>

</androidx.constraintlayout.widget.ConstraintLayout>

app:defaultNavHost="true" 端末の戻るボタンを制御する。
またデフォルトにできるのは通常1つのNavHostのみになります。

別Activityへの遷移

NavigationEditorのNew DestinationでActivityも追加できるので、
Activityを追加し、Fragmentからactionを追加してやる :sparkles:


Safe args

ドキュメント

Navigation Graphにargumentを追加

    <fragment android:id="@+id/secondFragment" android:name="slowhand.com.navigationsample.SecondFragment"
              android:label="fragment_second" tools:layout="@layout/fragment_second">
        <action android:id="@+id/action_secondFragment_to_mainFragment" app:destination="@id/mainFragment"/>
        <!-- こちら -->
        <argument android:name="text"
                  android:defaultValue="default"
                  app:argType="string" />
    </fragment>

argumentを渡す側

        buttonToSecond.setOnClickListener {
//            ↓の様にも書ける
//            val bundle = bundleOf("text" to "hello")
//            Navigation.findNavController(it).navigate(R.id.action_mainFragment_to_secondFragment, bundle)

            Navigation.findNavController(it).navigate(
                MainFragmentDirections.actionMainFragmentToSecondFragment(text = "hello"))
        }

argumentを受け取る側

textView.text = SecondFragmentArgs.fromBundle(arguments ?: return).text

XXXXFragmentDirectionsXXXXXFragmentArgs が自動生成される :sparkles:

DialogFragment

ダイアログのNavigationを行う方法です。
先に表示先のダイアログを作成します。

class SimpleDialogFragment : DialogFragment() {

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {

        return AlertDialog.Builder(requireActivity())
            .setTitle("")
            .setMessage("")
            .setPositiveButton("OK") { _, which ->
                ...
            }
            .create()
    }
}

Navigation Editorの「New Destination」から作成したDialogFragmentを追加します。
次にDialogに渡すパラメータを追加して行きます。

nav3.png

一回ビルドしなおすとパラメータをSafeArgsで受け取れるようになります。

val args = arguments?.let { SimpleDialogFragmentArgs.fromBundle(it) }

実際に使う場合は遷移元のFragmentをMainFragmentとすると

            Navigation.findNavController(it).navigate(
                MainFragmentDirections.actionMainFragmentToSimpleDialogFragment(arg = 0))

こんな感じで書けます :sparkles:

:bomb: バッドノウハウ


java.lang.IllegalArgumentException: navigation destination xxxxx.dev:id/action_xxxFragment_to_xxxDialogFragment is unknown to this NavController

こんなエラーが出た場合はDestinationの繋ぎ方を見直して間違ってないか要確認。


java.lang.IllegalStateException: View 
xxxx does not have a NavController set

DialogからDialog、またはFragmenntへ遷移しようとしていた為。
どうもDialogから他のFragmentへは遷移できなさそうでした。
stackoverflow
googleissue

繋ぎ方を見直しても間違ってなさそうな場合、以下の対応どちらかを検討する

  • currentDestinationを確認する
if (it.findNavController().currentDestination?.id == R.id.fragment_dashboard) {
    it.findNavController().navigate(R.id.fragment_detail)
}
  • エラーを握りつぶす
try { 
    it.findNavController().navigate(R.id.toDetails)
} catch (e: IllegalArgumentException) {
    // User tried tapping 2 links at once!
    Timber.e("Can't open 2 links at once!")
}

:link: 参考URL


6
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?