前提とやりたいこと
関係しそうな環境:
"androidx.navigation:navigation-fragment-ktx:2.3.0" "androidx.navigation:navigation-ui-ktx:2.3.0"
ツイッタークライアントを作っている。起動したらホームタイムラインを表示して、そこからツイートの詳細やユーザーのプロフィールのFragmentに遷移したり、会話を読んだりできる。また、自分で管理しているユーザーリストを表示して、そのユーザーリストのタイムラインを見ることもできる。他にもいくつか機能はあるけど、今回は割愛。
基本的な画面遷移の構成はいわゆるmaster/detail flowとかいうやつで、このアプリでは次の3つの遷移ができるようになっている。
- リスト(master)→詳細(detail)
- 詳細(detail)→リスト(master) 例:会話とかタグで検索(未実装だけどこれから作る)とか
- リスト(master)→リスト(master) 例:会話とかユーザーリスト(ナビゲーションドロワーから飛べる)とか
リストのFragmentにはどのエンドポイントを叩くかを決めるパラメータをArgumentとして渡している。論理的には無限に画面遷移できるので、たくさん画面遷移した後でも簡単にホームタイムラインに戻ってこられるように、ナビゲーションドロワーでホームタイムラインへ遷移する機能を作りたい。
困ったことと解決策
普通にandroidx.navigationのNavController.popBackstack(Int, Boolean)
を呼ぶと最初に見つかったFragment
までしか戻れない。
setGraph()
をやる時にバックスタックの状態をクリアするかな?と考えて内部の処理を読んでみたら、今まで持ってたグラフのIDとinclusive: true
を渡していたので、inclusive: false
に変えてやってみた。すると画面自体は巻き戻るものの、バックスタックの状態が変わった時に呼ばれるコールバックが呼ばれず、画面の状態に追従できなくなっていた。
実際のところ、これはナビゲーショングラフのルートまで戻る処理であって、ホームタイムラインのFragment
はNavController
のバックスタックからポップされている。ここでようやく、NavController
のバックスタックというのがFragment
のバックスタックとは違うのだという事に気づいた。
NavController
はcurrentBackstackEntry
というプロパティを持っていて、これがNavController
が管理しているバックスタックである。私はこれをFragment
が管理しているバックスタックエンティティ(か、あるいはそれに追従するもの)だと思っていたのだが実際のところはそこまで関連がない。
currentBackstackEntry
はFragment
のIDや渡したArgumentを持っている。また、popBackStack()
を呼んだらすぐに変わるので、ホームタイムラインを表示するためのクエリパラメータを持ったBackstackEntry
が出てくるまでpopBackStack()
を呼び続ければよい。