web-dev-qa-db-ja.com

BottomNavigationViewを新しいNavControllerで使用するときに、フラグメントを存続させる方法はありますか?

新しいナビゲーションコンポーネントを使用しようとしています。 BottomNavigationViewをnavControllerで使用します:NavigationUI.setupWithNavController(bottomNavigation、navController)

しかし、フラグメントを切り替えると、以前に使用されていたとしても、そのたびに破棄/作成されます。

BottomNavigationViewへのメインフラグメントリンクを維持する方法はありますか?

48
IdAndro

これを試して。

ナビゲーター

カスタムナビゲーターを作成します。

@Navigator.Name("custom_fragment")  // Use as custom tag at navigation.xml
class CustomNavigator(
    private val context: Context,
    private val manager: FragmentManager,
    private val containerId: Int
) : FragmentNavigator(context, manager, containerId) {

    override fun navigate(destination: Destination, args: Bundle?, navOptions: NavOptions?) {
        val tag = destination.id.toString()
        val transaction = manager.beginTransaction()

        val currentFragment = manager.primaryNavigationFragment
        if (currentFragment != null) {
            transaction.detach(currentFragment)
        }

        var fragment = manager.findFragmentByTag(tag)
        if (fragment == null) {
            fragment = destination.createFragment(args)
            transaction.add(containerId, fragment, tag)
        } else {
            transaction.attach(fragment)
        }

        transaction.setPrimaryNavigationFragment(fragment)
        transaction.setReorderingAllowed(true)
        transaction.commit()

        dispatchOnNavigatorNavigated(destination.id, BACK_STACK_DESTINATION_ADDED)
    }
}

NavHostFragment

カスタムNavHostFragmentを作成します。

class CustomNavHostFragment: NavHostFragment() {
    override fun onCreateNavController(navController: NavController) {
        super.onCreateNavController(navController)
        navController.navigatorProvider += PersistentNavigator(context!!, childFragmentManager, id)
    }
}

navigation.xml

フラグメントタグの代わりにカスタムタグを使用します。

<navigation xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto" Android:id="@+id/navigation"
    app:startDestination="@id/navigation_first">

    <custom_fragment
        Android:id="@+id/navigation_first"
        Android:name="com.example.sample.FirstFragment"
        Android:label="FirstFragment" />
    <custom_fragment
        Android:id="@+id/navigation_second"
        Android:name="com.example.sample.SecondFragment"
        Android:label="SecondFragment" />
</navigation>

アクティビティのレイアウト

NavHostFragmentの代わりにCustomNavHostFragmentを使用します。

<androidx.constraintlayout.widget.ConstraintLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    Android:id="@+id/container"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <fragment
        Android:id="@+id/nav_Host_fragment"
        Android:name="com.example.sample.CustomNavHostFragment"
        Android:layout_width="0dp"
        Android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@+id/bottom_navigation"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/navigation" />

    <com.google.Android.material.bottomnavigation.BottomNavigationView
        Android:id="@+id/bottom_navigation"
        Android:layout_width="0dp"
        Android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:menu="@menu/navigation" />

</androidx.constraintlayout.widget.ConstraintLayout>

更新

サンプルプロジェクトを作成しました。 リンク

カスタムNavHostFragmentを作成しません。 navController.navigatorProvider += navigatorを使用します。

36
STAR_ZERO

何時間も研究した後、解決策を見つけました。いつも目の前にありました:) backStackで見つかった場合、特定の宛先にナビゲートするpopBackStack(destination, inclusive)という関数があります。ブール値を返すため、コントローラーがフラグメントを見つけられない場合は、手動でナビゲートできます。

if(findNavController().popBackStack(R.id.settingsFragment, false)) {
        Log.d(TAG, "SettingsFragment found in backStack")
    } else {
        Log.d(TAG, "SettingsFragment not found in backStack, navigate manually")
        findNavController().navigate(R.id.settingsFragment)
    }
4
Piotr Prus

Googleサンプルのリンク NavigationExtensionsをアプリケーションにコピーして、サンプルごとに設定します。よく働く。

3
Zakhar Rodionov

@ piotr-prusが提供するソリューションは私を助けましたが、現在の宛先チェックを追加する必要がありました。

if (navController.currentDestination?.id == resId) {
    return       //do not navigate
}

このチェックなしでは、誤ってナビゲートした場合、バックスタックで見つからないため、現在の宛先が再作成されます。

0
akhris

@STAR_ZEROが提供するリンクを使用しましたが、正常に機能します。戻るボタンに問題がある場合は、アクティビティ/ナビゲーションホストでこのように処理できます。

override fun onBackPressed() {
        if(navController.currentDestination!!.id!=R.id.homeFragment){
            navController.navigate(R.id.homeFragment)
        }else{
            super.onBackPressed()
        }
    }

現在の宛先がルート/ホームフラグメント(通常、下部ナビゲーションビューの最初のフラグメント)であるかどうかを確認します。そうでない場合は、フラグメントに戻ります。

ところで、このソリューションはkeep_state_fragmentを使用して、STAR_ZEROが提供する上記のソリューションリンクと連携する必要があります。

0
veeyikpong

現在のところ利用できません。

回避策として、取得したすべてのデータをビューモデルに保存し、フラグメントを再作成するときにそのデータをすぐに使用できるようにすることができます。アクティビティコンテキストを使用してビューを取得してください。

LiveDataを使用して、データライフサイクルを認識できるようにすることができます

0
Samuel Robert