みなさんこんにちは!
Navigation Architecture Componentの作業の機能についてお話したいと思います。その理由は、ライブラリーの印象が曖昧だったからです。
この記事はステップバイステップのガイドではなく、実装の詳細を省略して重要な点に焦点を当てています。 インターネットにも
同様のユースケースが多数あります(翻訳もあります)-ライブラリを理解するのに役立ちます。 また、読む前に、
ドキュメントを勉強することを提案し
ます 。
ライブラリは間違いなく有用であり、誤用の可能性を排除するものではないことをすぐに言っておく必要がありますが、おそらくこの記事を書く前にすべてを試しました。そのため、機能の期待が実装の現実と一致しなかった実装のシナリオは次のとおりです。
メニュー項目を切り替える
これは、ナビゲーションコンポーネントを使用する決定に影響を与えた機能の1つです。
メニュー項目IDを同一にする必要があります
activity_main_drawer.xml<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:showIn="navigation_view"> <group android:checkableBehavior="single"> <item android:id="@+id/importFragment" android:icon="@drawable/ic_menu_camera" android:title="Import"/> <item android:id="@+id/galleryFragment" android:icon="@drawable/ic_menu_gallery" android:title="Gallery"/> <item android:id="@+id/slideshowFragment" android:icon="@drawable/ic_menu_slideshow" android:title="Slideshow"/>
およびスクリーンID(ナビゲーショングラフの目的地)
mobile_navigation.xml <?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/mobile_navigation" app:startDestination="@id/importFragment"> <fragment android:id="@+id/importFragment" android:name="com.xiii.navigationapplication.ImportFragment" android:label="fragment_import" tools:layout="@layout/fragment_import"/> <fragment android:id="@+id/galleryFragment" android:name="com.xiii.navigationapplication.GalleryFragment" android:label="fragment_gallery" tools:layout="@layout/fragment_gallery"/> <fragment android:id="@+id/slideshowFragment" android:name="com.xiii.navigationapplication.SlideshowFragment" android:label="fragment_slideshow" tools:layout="@layout/fragment_slideshow"/> </navigation>
次に、メニューをNavigation Controllerに関連付ける必要があります。
MainActivity.kt class MainActivity : AppCompatActivity() { private val navController by lazy(LazyThreadSafetyMode.NONE) { Navigation.findNavController(this, R.id.nav_host_fragment) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar)
メニュー内のナビゲーションが獲得されました-それは奇跡ではありませんか?!
「ハンバーガー」(メニューアイコン)に注意してください。メニュー項目を切り替えると、その状態が「戻る」ボタンに変わります。 この振る舞いは
珍しいようで(
おなじみ -プレイマーケットアプリケーションのように)、しばらくの間、何が間違っていたかを把握しようとしましたか?
それだけです! ナビゲーションの原則(つまり、ポイント
2および
3 )に関するドキュメントを読んだ後、「ハンバーガー」は
startDestinationに対してのみ表示されることに
気付きました 。 状況は、サブスクリプション(
addOnNavigatedListener() )でさまざまなトリックを適用して
宛先を変更することで変更できますが、説明することすらありません。 これはこのように機能します。条件に合わせる必要があります。
新しいアクティビティを開く
アクティビティは
ナビゲーションホストとして機能すると同時に、ナビゲーショングラフで
目的地の 1つとして機能でき
ます 。 ネストされたナビゲーショングラフなしでアクティビティを開くと、
期待どおりに動作
します、つまり呼び出し:
navController.navigate(R.id.editActivity)
(フラグメントの場合のように)遷移を行い、要求されたアクティビティを開きます。
ターゲットActivity自体が
ナビゲーションホストとして機能する場合、つまり
ドキュメントのオプション2の場合を考えると、さらに興味深いものになり
ます 。

例として、メモを追加するアクティビティを見てみましょう。 これに
は、EditFragment入力
フィールドを持つメインフラグメントが
含まれ、ナビゲーショングラフでは
startDestinationになります。 編集する際に写真を添付する必要があると仮定しましょう。そのためには、
PhotoFragmentに移動してカメラから画像を取得します。 ナビゲーショングラフは次のようになります。
edit_navigation.xml <?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/edit_navigation" app:startDestination="@id/editFragment"> <fragment android:id="@+id/editFragment" android:name="com.xiii.navigationapplication.ui.edit.EditFragment" android:label="fragment_edit" tools:layout="@layout/fragment_edit"> <action android:id="@+id/action_editFragment_to_photoFragment" app:destination="@id/photoFragment"/> </fragment> <fragment android:id="@+id/photoFragment" android:name="com.xiii.navigationapplication.ui.edit.PhotoFragment" android:label="fragment_photo" tools:layout="@layout/fragment_photo"/> </navigation>
EditActivityはMainActivityとそれほど変わりません。 主な違いは、
EditActivityにメニューがないことです。
EditActivity.kt class EditActivity : AppCompatActivity() { private val navController by lazy(LazyThreadSafetyMode.NONE) { Navigation.findNavController(this, R.id.nav_host_fragment) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_edit) setSupportActionBar(toolbar) // "" toolbar NavigationUI.setupWithNavController(toolbar, navController) } override fun onSupportNavigateUp() = navController.navigateUp() fun takePhoto(view: View) { navController.navigate(R.id.action_editFragment_to_photoFragment) } }
アクティビティが開き、その中のナビゲーションが機能します:
繰り返しますが、ツールバーのナビゲーションボタンに注意してください-開始
EditFragmentには「親アクティビティに戻る」ボタンはありません(しかし、私はしたいです)。 ドキュメントの観点から見ると、ここではすべてが合法です。新しいナビゲーショングラフ、新しい
startDestination値、「戻る」ボタンは
startDestinationの最後には表示されません。
親アクティビティを使用して
通常の動作に戻りたい場合は、フラグメント間の切り替え機能を維持しながら、このような
松葉杖のアプローチを提供できます。
1.マニフェストで親アクティビティを指定します <activity android:name=".EditActivity" android:parentActivityName=".MainActivity" android:theme="@style/AppTheme.NoActionBar"> <meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".MainActivity" /> </activity>
2. id startDestinationを置き換えるサブスクリプションを追加します class EditActivity : AppCompatActivity() { private val navController by lazy(LazyThreadSafetyMode.NONE) { Navigation.findNavController(this, R.id.nav_host_fragment) } private var isStartDestination = true override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_edit) setSupportActionBar(toolbar) val startDestinationId = navController.graph.startDestination // id, NavigationUI.ActionBarOnNavigatedListener // destination startDestination navController.addOnNavigatedListener { controller, destination -> isStartDestination = destination.id == startDestinationId // R.id.fake_start_destination id controller.graph.startDestination = if (isStartDestination) R.id.fake_start_destination else startDestinationId } // "" toolbar NavigationUI.setupActionBarWithNavController(this, navController) } override fun onSupportNavigateUp(): Boolean { // startDestination Navigation Component return if (isStartDestination) super.onSupportNavigateUp() else navController.navigateUp() } fun takePhoto(view: View) { navController.navigate(R.id.action_editFragment_to_photoFragment) } }
NavigationUI.ActionBarOnNavigatedListenerの すべての宛先が startDestinationにならないように、サブスクリプションが必要
です 。 したがって、
NavigationUI.ActionBarOnNavigatedListenerはナビゲーションボタンを非表示にしません(詳細についてはソースを参照してください)。 これに、
startDestinationで定期的に
onSupportNavigateUp()処理を追加し、
必要なものを取得します。
ライブラリの動作に対する明白でない介入であるという理由だけで、ソリューションが理想からかけ離れていることは言うに値します。
ディープリンクを使用すると問題が発生する可能性があると考えてい
ます (まだテストしていません)。
startDestinationにパラメーターを渡す
ナビゲーションコンポーネントには、ある
宛先から別の
宛先にパラメータ
を渡す
メカニズムがあります。 コード生成によって
型の安全性を
確保する
ツールもあり
ます (悪くない)。
次に、ケースを分析します。そのため、この機能にしっかりした5つを付けることができませんでした。
EditActivityに戻りましょう。これは、1つのActivityを使用してオブジェクトを作成および編集する、かなり馴染みのあるシナリオです。 Activityで編集するためにオブジェクトを開くとき、たとえば、オブジェクトのIDを転送する必要があります-通常の方法でそれをしましょう:
1. EditActivityのパラメーターをグラフに追加しますグラフのルート要素に直接パラメーターを追加しました(ナビゲーション)が、ターゲットフラグメントに追加できます。 これから、パラメータを取得する方法のみが変更されます。
<?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/edit_navigation" app:startDestination="@id/editFragment"> <argument android:name="id" app:argType="integer"/> <fragment android:id="@+id/editFragment" android:name="com.xiii.navigationapplication.ui.edit.EditFragment" android:label="fragment_edit" tools:layout="@layout/fragment_edit"> <action android:id="@+id/action_editFragment_to_photoFragment" app:destination="@id/photoFragment"/> </fragment> <fragment android:id="@+id/photoFragment" android:name="com.xiii.navigationapplication.ui.edit.PhotoFragment" android:label="fragment_photo" tools:layout="@layout/fragment_photo"/> </navigation>
2.アクションをメイングラフに追加します追加および編集アクションをフラグメントの1つに追加したので、フラグメントからのみ使用可能になります。
<?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/mobile_navigation" app:startDestination="@id/importFragment"> <fragment android:id="@+id/importFragment" android:name="com.xiii.navigationapplication.ImportFragment" android:label="fragment_import" tools:layout="@layout/fragment_import"> <action android:id="@+id/add" app:destination="@id/editActivity"> <argument android:name="id" app:argType="integer" android:defaultValue="0"/> </action> <action android:id="@+id/edit" app:destination="@id/editActivity"> <argument android:name="id" app:argType="integer"/> </action> </fragment> <fragment android:id="@+id/galleryFragment" android:name="com.xiii.navigationapplication.GalleryFragment" android:label="fragment_gallery" tools:layout="@layout/fragment_gallery"/> <fragment android:id="@+id/slideshowFragment" android:name="com.xiii.navigationapplication.SlideshowFragment" android:label="fragment_slideshow" tools:layout="@layout/fragment_slideshow"/> <activity android:id="@+id/editActivity" android:name="com.xiii.navigationapplication.EditActivity" android:label="activity_edit" tools:layout="@layout/activity_edit"/> </navigation>
3.パラメーターを準備し、移行をリクエストするこの例では、
ImportFragmentDirectionsは自動生成されたsafe-argsクラスです。
val direction = ImportFragmentDirections.edit(123 ) navController.navigate(direction)
3.フラグメントのIDを取得します class EditFragment : Fragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
確かに、あなたは
EditFragmentでパラメーターを取得する機能に注意を
払いました 。 これが機能するのは、編集アクション(ポイント1から)が引数を
EditActivityに渡すためであり、その理由のために、何らかの理由で、それをグラフに渡さない
ように貪欲です(たとえば、
navController.graph.setDefaultArguments()を呼び出して)。 この機能は、
Navigation Controllerを手動で準備することで回避できます。 1つの方法は
StackOwerflowで説明されてい
ます 。
startDestinationと通常の
宛先として同時に使用すると、おそらく最大の困難が発生し
ます 。 つまり
、このグラフの他の
宛先から
startDestinationにパラメーターを渡したり受け渡したりする場合、フラグメントは、引数またはintent.extrasからパラメーターを抽出する場所を個別に決定する必要があります。 これは、パラメータを渡すトランジションを設計するときに留意する必要があります。
要約すると、私自身はライブラリの使用を停止していないことに注意してください。リストされて
いる機能の
欠点にもかかわらず、使用することをお勧めするのに十分役立つと思います。 少なくとも
startDestinationにパラメーターを転送することで、次のリリースで状況が変わることを本当に願っています。
ご清聴ありがとうございました。 あなたの作業コード!
記事のソースは
GitHubに投稿され
ています 。