navigation

Android StudioのKotlinで、Bottom Navigationを使用している場合、各タブごとに独立したナビゲーションスタックを管理することが可能です。これを実現するための一般的なアプローチは、各タブごとに別々のNavHostFragmentを使用する方法です。

以下にその実装方法の一例を示します。

1. **各タブにNavHostFragmentを配置するレイアウトを作成する**:

```xml
<!-- res/layout/activity_main.xml -->
<LinearLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <FrameLayout
        android:id="@+id/nav_host_fragment_container"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_navigation_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:menu="@menu/bottom_nav_menu" />
</LinearLayout>
```

2. **各NavHostFragmentをレイアウトファイルとして定義する**:

```xml
<!-- res/layout/nav_host_home.xml -->
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/nav_host_fragment_home"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:defaultNavHost="false"
    app:navGraph="@navigation/nav_graph_home" />
```

```xml
<!-- res/layout/nav_host_dashboard.xml -->
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/nav_host_fragment_dashboard"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:defaultNavHost="false"
    app:navGraph="@navigation/nav_graph_dashboard" />
```

3. **Activityで各NavHostFragmentを切り替えるロジックを実装する**:

```kotlin
// MainActivity.kt
class MainActivity : AppCompatActivity() {

    private lateinit var bottomNavigationView: BottomNavigationView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        bottomNavigationView = findViewById(R.id.bottom_navigation_view)

        if (savedInstanceState == null) {
            supportFragmentManager.beginTransaction()
                .replace(R.id.nav_host_fragment_container, NavHostFragment.create(R.navigation.nav_graph_home))
                .commit()
        }

        bottomNavigationView.setOnItemSelectedListener { item ->
            when (item.itemId) {
                R.id.navigation_home -> {
                    switchToFragment(R.navigation.nav_graph_home)
                    true
                }
                R.id.navigation_dashboard -> {
                    switchToFragment(R.navigation.nav_graph_dashboard)
                    true
                }
                R.id.navigation_notifications -> {
                    switchToFragment(R.navigation.nav_graph_notifications)
                    true
                }
                else -> false
            }
        }
    }

    private fun switchToFragment(navGraphId: Int) {
        val existingFragment = supportFragmentManager.findFragmentByTag(navGraphId.toString())
        if (existingFragment != null) {
            supportFragmentManager.beginTransaction()
                .replace(R.id.nav_host_fragment_container, existingFragment)
                .commit()
        } else {
            val navHostFragment = NavHostFragment.create(navGraphId)
            supportFragmentManager.beginTransaction()
                .replace(R.id.nav_host_fragment_container, navHostFragment, navGraphId.toString())
                .commit()
        }
    }
}
```

4. **BottomNavigationViewのメニューリソースを作成する**:

```xml
<!-- res/menu/bottom_nav_menu.xml -->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/navigation_home"
        android:icon="@drawable/ic_home"
        android:title="Home" />
    <item
        android:id="@+id/navigation_dashboard"
        android:icon="@drawable/ic_dashboard"
        android:title="Dashboard" />
    <item
        android:id="@+id/navigation_notifications"
        android:icon="@drawable/ic_notifications"
        android:title="Notifications" />
</menu>
```

このアプローチにより、各タブのナビゲーションスタックが独立して管理されるため、特定のフラグメントに戻る際に`popBackStack`を使用しても他のタブには影響を与えません。


上記の実装を使用すると、各タブに独立したナビゲーションスタックを持たせることができます。ただし、標準的なナビゲーション実装では、タブ間のナビゲーションスタックを統一して管理することはできません。

質問のように、以下のシナリオを実現するには追加のカスタマイズが必要です:
1. タブAの画面1 → タブAの画面2
2. タブBの画面1 → タブBの画面2
3. タブCの画面1
4. タブAの画面2に戻る
5. タブBの画面2に戻る

各タブが独立したナビゲーションスタックを持つ場合、以下のような拡張が必要です：

1. **ナビゲーションスタックの管理を拡張する**: 各タブのナビゲーションスタックを管理するために、`FragmentManager`のバックスタックを直接操作する。
2. **タブごとのスタックを保存する**: 各タブのナビゲーションスタックの状態を保存・復元するロジックを実装する。

ここに具体的な実装例を示します：

```kotlin
// MainActivity.kt
class MainActivity : AppCompatActivity() {

    private lateinit var bottomNavigationView: BottomNavigationView
    private val fragmentStacks = mutableMapOf<Int, Stack<Fragment>>()
    private var currentTabId: Int = R.id.navigation_home

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        bottomNavigationView = findViewById(R.id.bottom_navigation_view)
        bottomNavigationView.setOnItemSelectedListener { item ->
            switchToTab(item.itemId)
            true
        }

        if (savedInstanceState == null) {
            initializeStacks()
            switchToTab(R.id.navigation_home)
        }
    }

    private fun initializeStacks() {
        fragmentStacks[R.id.navigation_home] = Stack()
        fragmentStacks[R.id.navigation_dashboard] = Stack()
        fragmentStacks[R.id.navigation_notifications] = Stack()
    }

    private fun switchToTab(tabId: Int) {
        if (tabId == currentTabId) return

        val fragmentManager = supportFragmentManager
        val currentStack = fragmentStacks[currentTabId]
        val newStack = fragmentStacks[tabId]

        fragmentManager.beginTransaction().apply {
            currentStack?.lastElement()?.let { hide(it) }
            newStack?.lastElement()?.let { show(it) }
            commit()
        }

        currentTabId = tabId
    }

    fun pushFragment(fragment: Fragment) {
        val fragmentManager = supportFragmentManager
        val currentStack = fragmentStacks[currentTabId]

        fragmentManager.beginTransaction().apply {
            currentStack?.lastElement()?.let { hide(it) }
            add(R.id.nav_host_fragment_container, fragment)
            addToBackStack(null)
            commit()
        }

        currentStack?.push(fragment)
    }

    override fun onBackPressed() {
        val currentStack = fragmentStacks[currentTabId]
        if (currentStack != null && currentStack.size > 1) {
            val fragmentManager = supportFragmentManager
            fragmentManager.beginTransaction().apply {
                currentStack.pop()?.let { remove(it) }
                currentStack.lastElement()?.let { show(it) }
                commit()
            }
        } else {
            super.onBackPressed()
        }
    }
}
```

**説明**:
1. **Fragmentの管理**: 各タブのフラグメントスタックを`fragmentStacks`で管理します。
2. **タブの切り替え**: タブを切り替える際に、現在のタブのフラグメントを非表示にし、次のタブの最後のフラグメントを表示します。
3. **フラグメントのプッシュ**: 新しいフラグメントを追加する際に、現在のフラグメントを非表示にし、バックスタックに追加します。
4. **戻るボタンの処理**: 現在のタブのスタックを管理し、スタックからポップして表示を更新します。

このようにして、各タブのナビゲーションスタックを独立して管理しながら、ユーザーがタブを切り替える際に適切なスタックの状態を維持できます。