英文:
Android Navigation Component load 2 nested Fragments into Parent Fragment
问题
我有一个比较器屏幕,是一个分片的片段,分为2个子屏幕。在使用导航组件之前,我可以轻松地初始化片段:
private void initializeFragments() {
FoodComparatorFragment comparatorFragment1 = new FoodComparatorFragment();
FoodComparatorFragment comparatorFragment2 = new FoodComparatorFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.add(R.id.comparator_fragment_1, comparatorFragment1);
transaction.add(R.id.comparator_fragment_2, comparatorFragment2);
transaction.commit();
}
但是,我不知道在不明确使用FragmentManager的情况下,应该如何执行此操作。
英文:
I have a Comparator Screen which is a Fragment that is splitted into 2 sub-screens. Before using Navigation Component I could easily just:
private void initializeFragments() {
FoodComparatorFragment comparatorFragment1 = new FoodComparatorFragment();
FoodComparatorFragment comparatorFragment2 = new FoodComparatorFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.add(R.id.comparator_fragment_1, comparatorFragment1);
transaction.add(R.id.comparator_fragment_2, comparatorFragment2);
transaction.commit();
}
However, I don't know how I should perform this operation with the Navigation Component WITHOUT using explicitly the FragmentManager
.
答案1
得分: 1
以下是已翻译的内容:
默认情况下,使用导航组件时,主活动(main activity)托管了一个NavHostFragment
,适用于一种活动(activity)和多个片段(fragments)的模型。对于多个活动和片段的模型,每个活动都有自己的NavHostFragment
和NavGraph
。
在这里,我们将考虑一种活动和多个片段的模型,其中其中一个片段是比较器屏幕(父片段),分为2个子屏幕/片段。
但是,NavHostFragment
一次只能托管一个片段;所以这里的想法是在比较器/父片段中有两个NavHostFragments
。
考虑以下示例:
- 我们有一个托管两个片段(
HomeFragment
和ParentFragment
)的MainActivity
。 ParentFragment
是比较器,有两个NavHostFragments
,每个都可以托管一个片段;顶部是TopFragment
,底部是BottomFragment
。- 有一个从
HomeFragment
到ParentFragment
的操作。 - 每个子屏幕都接收到一个
int
参数,即顶部和底部片段,通过将参数与从HomeFragment
到ParentFragment
的操作一起发送到子屏幕;它们将显示在屏幕上的TextViews
上。
现在我们有3个NavGraphs
:
主(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/navigation_home">
<fragment
android:id="@+id/navigation_home"
android:name="com.example....HomeFragment"
android:label="@string/title_home"
tools:layout="@layout/fragment_home">
<action
android:id="@+id/action_homeFragment_to_parentFragment"
app:destination="@id/navigation_parent"
app:popUpTo="@id/navigation_home"
app:popUpToInclusive="false" />
</fragment>
<fragment
android:id="@+id/navigation_parent"
android:name="com.example....ParentFragment"
android:label="@string/title_parent"
tools:layout="@layout/fragment_parent">
<argument
android:name="top_arg"
android:defaultValue="-1"
app:argType="integer" />
<argument
android:name="bottom_arg"
android:defaultValue="-1"
app:argType="integer" />
</fragment>
</navigation>
在ParentFragment
中,还有另外两个NavGraphs
,分别用于子屏幕:
对于第一个子屏幕(top_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/top_navigation"
app:startDestination="@+id/top_fragment">
<fragment
android:id="@+id/top_fragment"
android:name="com.example.....TopFragment"
tools:layout="@layout/fragment_top">
<argument
android:name="arg"
android:defaultValue="-1"
app:argType="integer" />
</fragment>
</navigation>
对于第二个子屏幕(bottom_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/bottom_navigation"
app:startDestination="@+id/bottom_fragment">
<fragment
android:id="@+id/bottom_fragment"
android:name="com.example.....BottomFragment"
tools:layout="@layout/fragment_bottom">
<argument
android:name="arg"
android:defaultValue="-1"
app:argType="integer" />
</fragment>
</navigation>
布局(Layouts):
activity_main.xml
:
<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">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment_activity_main"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/mobile_navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>
fragment_parent.xml
:
<androidx.constraintlayout.widget.ConstraintLayout 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"
tools:context=".ui.fragments.HomeFragment">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/top_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/teal_200"
app:layout_constraintBottom_toTopOf="@id/bottom_nav_host_fragment"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/bottom_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/purple_200"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraint
<details>
<summary>英文:</summary>
By default with navigation components there is one `NavHostFragment` hosted by the main activity for one activity/many fragments model. For many activities/fragments model, each activity has its own `NavHostFragment/NavGraph`.
Here we'll consider one activity, many fragments model, where one of these fragments is the Comparator Screen (parent fragment) that is splitted into 2 sub-screens/fragments.
But the `NavHostFragment` can only host a single fragment at a time; so the idea here is to have two `NavHostFragments` within the comparator/parent fragment.
-----
## Considering the below example:
- We have a `MainActivity` that hosts two fragments (`HomeFragment`, `ParentFragment`).
- The `ParentFragment` is the comparator with two `NavHostFragments`, each can host a single fragment; the top one is `TopFragment`, the bottom is `BottomFragment`.
- There is an action to go from the `HomeFragment` to the `ParentFragment`
- There is `int` argument sent to each sub-screen, the Top/Bottom fragments, done by sending arguments to the sub-screens along with the action from the `HomeFragment` to the `ParentFragment`; they will be displayed on `TextViews` on the screen.
MainActivity (1 NavHostFragments/NavGraph) ->> nav_host_fragment_activity_main
|
|---- HomeFragment
|
|---- ParentFragment (2 NavHostFragments/NavGraphs) ->> top_nav_host_fragment & bottom_nav_host_fragment
| |
| |---- TopFragment
| |
| |---- BottomFragment
### **Now we have 3 NavGraphs:**
Main (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/navigation_home">
<fragment
android:id="@+id/navigation_home"
android:name="com.example....HomeFragment"
android:label="@string/title_home"
tools:layout="@layout/fragment_home">
<action
android:id="@+id/action_homeFragment_to_parentFragment"
app:destination="@id/navigation_parent"
app:popUpTo="@id/navigation_home"
app:popUpToInclusive="false" />
</fragment>
<fragment
android:id="@+id/navigation_parent"
android:name="com.example....ParentFragment"
android:label="@string/title_parent"
tools:layout="@layout/fragment_parent">
<argument
android:name="top_arg"
android:defaultValue="-1"
app:argType="integer" />
<argument
android:name="bottom_arg"
android:defaultValue="-1"
app:argType="integer" />
</fragment>
</navigation>
[![enter image description here][1]][1]
**And in `ParentFragment`, another two `NavGraphs`, each for a sub-screen:**
For the 1st sub-screen (top_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/top_navigation"
app:startDestination="@+id/top_fragment">
<fragment
android:id="@+id/top_fragment"
android:name="com.example.....TopFragment"
tools:layout="@layout/fragment_top">
<argument
android:name="arg"
android:defaultValue="-1"
app:argType="integer" />
</fragment>
</navigation>
For the 2nd sub-screen (bottom_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/bottom_navigation"
app:startDestination="@+id/bottom_fragment">
<fragment
android:id="@+id/bottom_fragment"
android:name="com.example.....BottomFragment"
tools:layout="@layout/fragment_bottom">
<argument
android:name="arg"
android:defaultValue="-1"
app:argType="integer" />
</fragment>
</navigation>
**Layouts:**
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment_activity_main"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/mobile_navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>
fragment_parent.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
tools:context=".ui.fragments.HomeFragment">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/top_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/teal_200"
app:layout_constraintBottom_toTopOf="@id/bottom_nav_host_fragment"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/bottom_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/purple_200"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/top_nav_host_fragment" />
</androidx.constraintlayout.widget.ConstraintLayout>
fragment_top/bottom.xml are straightforward
fragment_top.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
tools:context=".ui.fragments.ParentFragment">
<TextView
android:id="@+id/textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:textAlignment="center"
android:text="top fragment"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
The `NavGraph` of the Top & Bottom fragments will be set programmatically in order to allow passing the arguments from the main `NavGraph` to the sub-screens' `NavGraphs`.
Now in the `HomeFragment`, there is a button to go to the `ParentFragment` with the action defined in the `NavGraph`, which sends the arguments needed for both sub-screens:
findNavController().navigate(
HomeFragmentDirections.actionHomeFragmentToParentFragment()
.setTopArg(100)
.setBottomArg(200))
Then receive the arguments at the `ParentFragment` with the `SafeArgs`:
// get fragment arguments through SafeArgs
val args = ParentFragmentArgs.fromBundle(requireArguments())
val bottomBundle = Bundle()
bottomBundle.putInt("arg", args.bottomArg)
val topBundle = Bundle()
topBundle.putInt("arg", args.topArg)
// setting the navGraph of the top/bottom NavHostFragments,
// and pass the argument along as a bundle.
val bottomNavHostFragment =
childFragmentManager.findFragmentById(R.id.bottom_nav_host_fragment)
as NavHostFragment
bottomNavHostFragment.navController.setGraph(R.navigation.bottom_navigation, bottomBundle)
val topNavHostFragment = childFragmentManager.findFragmentById(R.id.top_nav_host_fragment)
as NavHostFragment
topNavHostFragment.navController.setGraph(R.navigation.top_navigation, topBundle)
Once more at the destination top/bottom sub-screens, get the argument with `safeArgs`, and set it to the `TextView`:
// TopFragment
textView.text = TopFragmentArgs.fromBundle(
requireArguments()
).arg.toString()
// BottomFragment
textView.text = BottomFragmentArgs.fromBundle(
requireArguments()
).arg.toString()
[![enter image description here][2]][2]
[1]: https://i.stack.imgur.com/pek44.png
[2]: https://i.stack.imgur.com/Id25n.png
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论