Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Navigation Componentで先に知っておきたかったポイント
Search
Takaki Hoshikawa
November 21, 2019
Programming
7
2.4k
Navigation Componentで先に知っておきたかったポイント
* Navigation Component が、内部でどのような挙動をしているのか解説します
* 挙動を理解した後に Navigation Component でできることや、制約について話します
Takaki Hoshikawa
November 21, 2019
Tweet
Share
More Decks by Takaki Hoshikawa
See All by Takaki Hoshikawa
明日からはじめるオンライン勉強会
oboenikui
0
66
Flutterを使って医師相談アプリを短期リリースした話
oboenikui
4
2.4k
Amazon Dash Buttonの話
oboenikui
0
120
Jetpack Security
oboenikui
5
1.2k
Kotlin/NativeのiOSにおけるオーバーヘッド
oboenikui
6
2.8k
Kotlin_2FNative__使ってみた_の一歩先へ.pdf
oboenikui
0
46
アプリをエミュレートするアプリの登場とその危険性 / How multi-account app works #DroidKaigi
oboenikui
0
78
Multiplatform Projectsの向かう先 #m3kt
oboenikui
0
510
Starting Kotlin/Native おなじところ、ちがうところ #m3kt
oboenikui
1
7k
Other Decks in Programming
See All in Programming
「今のプロジェクトいろいろ大変なんですよ、app/services とかもあって……」/After Kaigi on Rails 2024 LT Night
junk0612
5
2.2k
ローコードSaaSのUXを向上させるためのTypeScript
taro28
1
630
Duckdb-Wasmでローカルダッシュボードを作ってみた
nkforwork
0
130
TypeScriptでライブラリとの依存を限定的にする方法
tutinoko
3
700
flutterkaigi_2024.pdf
kyoheig3
0
150
Flutterを言い訳にしない!アプリの使い心地改善テクニック5選🔥
kno3a87
1
210
見せてあげますよ、「本物のLaravel批判」ってやつを。
77web
7
7.8k
リアーキテクチャxDDD 1年間の取り組みと進化
hsawaji
1
220
AWS Lambdaから始まった Serverlessの「熱」とキャリアパス / It started with AWS Lambda Serverless “fever” and career path
seike460
PRO
1
260
Figma Dev Modeで変わる!Flutterの開発体験
watanave
0
150
ふかぼれ!CSSセレクターモジュール / Fukabore! CSS Selectors Module
petamoriken
0
150
Why Jakarta EE Matters to Spring - and Vice Versa
ivargrimstad
0
1.2k
Featured
See All Featured
Building Flexible Design Systems
yeseniaperezcruz
327
38k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
93
16k
What’s in a name? Adding method to the madness
productmarketing
PRO
22
3.1k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
126
18k
Ruby is Unlike a Banana
tanoku
97
11k
Building Applications with DynamoDB
mza
90
6.1k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
28
9.1k
Building a Scalable Design System with Sketch
lauravandoore
459
33k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
329
21k
Making Projects Easy
brettharned
115
5.9k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
33
1.9k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
Transcript
None
• 星川 貴樹 (@oboenikui) • エムスリー株式会社所属 (新卒3年目) • すき ◦
Android ◦ プロ野球観戦 ◦ ネタTシャツ お ぼ え に く い
• 会社で作っている新規アプリにNavigation Componentを導入しま した • 機能紹介記事は多くありますが、動作に関する解説があまり多くなさ そうだった & 動作を知らないためにモヤモヤしたことがありました •
Navigation Componentがどのように動作しているかを理解したら スッキリ!
• Navigation Component が、内部でどのような挙動をしているの か解説します • 挙動を理解した後に Navigation Component でできることや、
制約について話します
• 特に言及されてないところは 2.2.0-rc02 での情報です • 標準の機能(想定される使い方)として提供されていないという意味 で「できない」という表現を使います • コードなどはスペースの都合上端折って書いているところがあります
None
• 画面遷移を、決まった原則に従っ て実装するためのライブラリ • 原則や実装方法などはここ1年で多 くの方が解説してくださっている ので割愛 https://developer.android.com/guide/navigation https://droidkaigi.jp/2019/timetable/70415 https://developer.android.com/guide/navigation
/navigation-getting-started より引用
• 1つのActivityにおけるFragmentや他Activityへの遷移をXMLで 定義する • 複雑なFragment遷移を良い感じにコントロールしてくれる • Navigation要素 (ToolbarやBottomNavigationなど) と連携 して良い感じの遷移を簡単に記述できる
• なぜpopUpToオプションに <navigation> のIDを指定する場合が ある? • Fragmentをキャッシュできないの? • マルチモジュール構成のプロジェクトでのベストプラクティスは? •
…… 仕組みを先に理解しないとモヤモヤが残る
None
• XMLで定義したグラフ(木構造) • <navigation> が内部ノード, <fragment> <activity> <dialog> が葉ノードになる ◦
これらのノードをNavDestinationと呼ぶ • <action> <argument> <deepLink> は別扱い
• NavController#navigate メソッドでdestinationとして指定 できるもの ◦ ただし <action> <deepLink> は別物 •
<action> <deepLink> <argument> を持つことが可能 ◦ これらはNavDestinationのプロパティみたいなもの ◦ <action> に関しては <activity> では使えない
<navigation android:id="@+id/navigation_a" app:startDestination="@+id/fragment_a1"> <fragment android:id="@+id/fragment_a1"/> <fragment android:id="@+id/fragment_a2"/> <navigation android:id="@+id/navigation_b" app:startDestination="@id/fragment_b1">
<fragment android:id="@+id/fragment_b1"/> <fragment android:id="@+id/fragment_b2"/> </navigation> <navigation android:id="@+id/navigation_c" app:startDestination="@id/fragment_c"> <fragment android:id="@+id/fragment_c"/> <activity android:id="@+id/activity_c"/> </navigation> </navigation>
None
Actionは遷移時のオプションをまと めたショートカットみたいなもの Actionを使わずに遷移もできるがこ こでは説明の簡略化のため割愛する
【見かけの挙動】 <navigation> へのactionでは Start Destinationへ更に遷移する ここでは見かけ上 <fragment A1> → <fragment
B1> という遷移
【本質的な話】 <fragment A1> は親ノードである <navigation A> の子ノードだったら 遷移可能。そのため <fragment A1>
→ <navigation B> の遷移が可能 親の子ノード 親の子ノード ではない者達 親
None
【見かけの挙動】 <fragment B2> から <fragment C> への遷移
親の子ノード 親の子ノードではない者達 親の子ノード ではない者達 親 【本質的な話】 <fragment B2> は親の親ノードであ る
<navigation A> の子ノードだっ たら遷移可能。そのため <fragment B2> → <navigation C> の遷移が可能
親の親の 子ノード 親 親の親 【本質的な話】 <fragment B2> は親の親ノードであ る <navigation
A> の子ノードだっ たら遷移可能。そのため <fragment B2> → <navigation C> の遷移が可能 親や親の親の子ノードではない者達 親の子ノード
None
親 親の子ノード ではない者達 親の子ノード 【本質的な話】 先祖ノードから辿れないノードへは Actionでは直接遷移できない
親が知らない 行き先
親が知らない 行き先 Deep Linkは遷移可能なノードを ルートから全探索するので理論上 どこへも遷移できる ルート 誰かこのDeep Link扱えない?
親が知らない 行き先 Deep Linkは遷移可能なノードを ルートから全探索するので理論上 どこへも遷移できる ルート 誰かこのDeep Link扱えない? 無理
無理 わからん、聞いてみる わからん、聞いてみる
親が知らない 行き先 Deep Linkは遷移可能なノードを ルートから全探索するので理論上 どこへも遷移できる ルート 誰かこのDeep Link扱えない? 無理
無理 無理 できるよ 誰かこのDeep Link扱えない?
• XMLをパースしてNavGraphを構築したり、実際の遷移処理を行うク ラス • NavControllerはFragmentManagerとは別に、遷移した NavDestinationをBack stackとして保持する
NavControllerの Back stackのイメージ
<navigation> タグも スタックに積まれる
None
None
None
None
None
None
None
None
None
None
None
None
Activity遷移は管理する必要 がないのでstackに積まない
None
積み残った<navigation>も 同時に整理される
• popUpToで指定したdestinationがBack stackに積まれていた場 合は、そこまでpopする機能 • popUpToInclusive = true ならそのdestinationもpopする popUpToInclusive
= false ならその手前までpopする
popUpTo に <navivation A> を設定した場合 ここまでの stackを popする
popUpTo に <navivation A> を設定した場合
popUpTo に <navivation A> を設定した場合
popUpTo に <navivation A> を設定した場合 popUpTo にルートの <navigation> を指定 するのは、Back
stackをクリアするのと同義
None
• <navigation> 直下に <action> を設定すると、どの子孫ノードからも 実行できるActionが作られる <navigation ...> <fragment android:id="@+id/frag_a1">
<!-- Fragment A1からのみ実行可能 --> <action app:destination="@id/actv_a"/> </fragment> <fragment android:id="@+id/frag_a2"/> <activity android:id="@+id/actv_a"/> </navigation> よく解説ブログに出てくる Action
• <navigation> 直下に <action> を設定すると、どの子孫ノードからも 実行できるActionが作られる <navigation ...> <fragment android:id="@+id/frag_a1"/>
<fragment android:id="@+id/frag_a2"/> <activity android:id="@+id/actv_a"/> <!-- どのFragmentからも実行可能 --> <action android:id="@+id/action_actv_a" app:destination="@id/actv_a" /> </navigation> Global Action
Google I/Oでの発表によると、 • 各モジュールごとにNavigation XML を作る • メインモジュールのNavigation XML でincludeする
のが想定解の模様 https://youtu.be/JFGq0asqSuA?t=1130
Google I/Oでの発表によると、 • 各モジュールごとにNavigation XML を作る • メインモジュールのNavigation XML でincludeする
のが想定解の模様 https://youtu.be/JFGq0asqSuA?t=1130 <!-- main_navigation.xml --> <navigation ...> <include app:graph="@navigation/login_navigation" /> <include app:graph="@navigation/home_navigation" /> ... </navigation>
<!-- main_navigation.xml --> <navigation ...> <include app:graph="@navigation/login_navigation" /> <include app:graph="@navigation/home_navigation"
/> ... </navigation>
<!-- main_navigation.xml --> <navigation ...> <navigation android:id="@+id/login_navigation" /> <navigation android:id="@+id/home_navigation"
/> ... </navigation>
• 2.3.0から?使えるようになるみたいです • 現在SNAPSHOT版が公開されてます https://developer.android.com/guide/navigation/navigation-dynamic
None
• 例えば右のようなアプリで と遷移した後、バックボタンを押したらどう遷移 するのが理想か • Navigationではスタック操作で可能な遷移のみ 実現できるので以下のような遷移ができない
• BottomNavigationView などのナビゲーション用Viewで遷移する 場合、Fragmentが新たに生成される ◦ Google Newsアプリが近い挙動 • popBackStack しない限り生成済みのFragmentには戻らない、と
いう挙動が徹底されている • ViewModelに表示に関する全てのデータを持たせ、Fragmentはそ れに従って表示するだけ、というのを徹底する思想?
SafeArgs Pluginの問題だが、includeしたNavigation XML内で定 義された <argument> を読んでくれない
<!-- main_navigation.xml --> <navigation ...> <include app:graph="@navigation/sub_navigation"/> <action android:id="@+id/action_main_to_sub" app:destination="@+id/frag_sub"/>
</navigation> <!-- sub_navigation.xml --> <navigation ...> <fragment android:id="@+id/frag_sub"> <argument android:name="arg" app:argType="string" .../> </fragment> </navigation>
// 理想: Stringを引数に取るメソッドが生成されてほしい MainNavigationDirections.actionMainToSub("hoge")
// 理想: Stringを引数に取るメソッドが生成されてほしい MainNavigationDirections.actionMainToSub("hoge") // 現実: 引数を何も取らないメソッドが生成される MainNavigationDirections.actionMainToSub()
<!-- main_navigation.xml --> <navigation ...> <include app:graph="@navigation/sub_navigation"/> <action android:id="@+id/action_main_to_sub" app:destination="@+id/frag_sub">
<argument android:name="arg" app:argType="string" .../> <!-- ここ --> </action> </navigation> <!-- sub_navigation.xml --> <navigation ...> <fragment android:id="@+id/frag_sub"> <argument android:name="arg" app:argType="string" .../> </fragment> </navigation>
navigation-safe-args-generatorに実装があるので最新の実装は こちらから確認できます https://android.googlesource.com/platform/frameworks/support/+/androidx-m aster-dev/navigation/navigation-safe-args-generator/
• Navigation Component の動作について解説をした • Navigation Component の中の事情を知った上で、 何ができるか/できないかについて解説した •
Android Advent Calendar にも関連記事を書く予定 • 実験用に作ったサンプルリポジトリはこちら (最新版をpushし忘れたので後で確認してください^^;) https://github.com/oboenikui/navigation-experiment
<navigation android:id="@+id/navigation_a" app:startDestination="@+id/fragment_a1"> <fragment android:id="@+id/fragment_a1" .../> <fragment android:id="@+id/fragment_a2" .../> <navigation
android:id="@+id/navigation_b" ...> <navigation android:id="@+id/navigation_c" ...> </navigation>
<navigation android:id="@+id/navigation_a" app:startDestination="@+id/fragment_a1"> <fragment android:id="@+id/fragment_a1" .../> <fragment android:id="@+id/fragment_a2" .../> <navigation
android:id="@+id/navigation_b" ...> <navigation android:id="@+id/navigation_c" ...> </navigation>
<navigation android:id="@+id/navigation_a" ...> <fragment .../> <fragment .../> <navigation android:id="@+id/navigation_b" app:startDestination="@+id/fragment_b1">
<fragment android:id="@+id/fragment_b1" .../> <fragment android:id="@+id/fragment_b2" ...> </navigation> <navigation android:id="@+id/navigation_c" ...> </navigation>
<navigation android:id="@+id/navigation_a" app:startDestination="@+id/fragment_a1"> <fragment android:id="@+id/fragment_a1" .../> <fragment android:id="@+id/fragment_a2" .../> <navigation
android:id="@+id/navigation_b" ...> <navigation android:id="@+id/navigation_c" ...> </navigation>
<navigation android:id="@+id/navigation_a" ...> <fragment .../> <fragment .../> <navigation android:id="@+id/navigation_b" ...>
<navigation android:id="@+id/navigation_c" app:startDestination="@+id/fragment_c"> <fragment android:id="@+id/fragment_c" .../> <activity android:id="@+id/activity_c" ...> </navigation> </navigation>