Reputation: 7996
I'm working on a simple app to test out JetPack Navigation in Android. My App have two activities "Activity1" and "Activity2". I'm aware that Jetpack navigation only works for SingleActivity apps but I want to see if there is any solution to my problem. App starts with "Activity1". Below is the navigation xml
<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_nav"
app:startDestination="@+id/fragmentA1">
<fragment
android:id="@+id/fragmentA1"
android:name="com.example.android.codelabs.navigation.FragmentA1"
android:label="@string/home"
tools:layout="@layout/fragment_a_one">
<action
android:id="@+id/go_to_fragmentA2"
app:destination="@+id/fragmentA2"/>
</fragment>
<fragment
android:id="@+id/fragmentA2"
android:name="com.example.android.codelabs.navigation.FragmentA2"
tools:layout="@layout/fragment_a_two">
<argument
android:name="flowStepNumber"
app:argType="integer"
android:defaultValue="1"/>
<argument
android:name="displayName"
app:argType="string"
android:defaultValue="Hello"/>
<action
android:id="@+id/go_to_fragmentA3"
app:destination="@+id/fragmentA3"/>
</fragment>
<fragment android:id="@+id/fragmentA3"
android:name="com.example.android.codelabs.navigation.FragmentA3"
android:label="fragment_third"
tools:layout="@layout/fragment_a_three">
<action android:id="@+id/go_to_activityB"
app:destination="@id/activityB"/>
</fragment>
<activity android:id="@+id/activityB"
android:name="com.example.android.codelabs.navigation.registration.ActivityB"
android:label="activity_registration_main"
tools:layout="@layout/activity_registration_main">
<argument android:name="firstName"
app:argType="string"/>
</activity>
<fragment android:id="@+id/fragmentB1"
android:name="com.example.android.codelabs.navigation.registration.FragmentB1"
android:label="fragment_step1"
tools:layout="@layout/fragment_b_one">
<argument android:name="firstName"
app:argType="string"/>
</fragment>
<fragment android:id="@+id/fragmentB2"
android:name="com.example.android.codelabs.navigation.registration.FragmentB2"
android:label="fragment_step2"
tools:layout="@layout/fragment_b_two">
<argument android:name="lastName"
app:argType="string"/>
</fragment>
Below is the code from ActivityB
val navController = findNavController(R.id.registration_navHost)
navController.setGraph(R.navigation.mobile_navigation)
navController.navigate(R.id.fragmentB1,safeArgs.toBundle())
Code to get safeArgs in ActivityB
private val safeArgs by navArgs<ActivityBArgs>()
Here are my questions. 1. How can I navigate from ActivityB(FragmentB1) to ActivityA(FragmentA2) Using jetpack Navigation. 2. Is there any way to access the backStack through any Manager Classes in android ?
Why Do I Need Two Activities? FragmentA1,FragmentA2 and FragmentA3 which are part of ActivityA shouldn't have any bottom navigation and drawer layout. Depending on answers selected in these fragments, a bottom navigation and a drawer layout are displayed in ActivityB. On Pressing back button from anywhere on ActivityB, ActivityA should come alive with FragmentA3. Also the toolBar is different for ActivityA and ActivityB
Upvotes: 4
Views: 2058
Reputation: 5059
Activities can now be items within Navigation Graphs and can be navigated to using destinations. Since around Jetpack Navigation 2.3.0? Support is limited though, still room for new feature requests.
Upvotes: 0
Reputation: 377
Despite having two Activities, then again I recommend Single Activity apps. I had some testing to separate the Activity because one had BottomNavigation and one without BottomNavigation (Like your issue here). But I always stuck to navigate between the Activities and the backstack always reset (as far as I test it).
Other solution would be Nested NavigationGraph, but I avoid some nested because I think it'll mingle the navigation. So yeah, I try to find another approach and here's what I test and use now.
Let's make a new scenario. I have 3 page that has BottomNavigation (Dashboard, Search, Profile). Then I have a page that doesn't has BottomNavigation, ex. ProfileDetail that opened from Profile. So in this scenario, I'll create:
I change all my layout became Fragments and when it navigates to any layout, it will check wether they need the BottomNavigation. The basic concept is Show / Hide
the BottomNavigation. Here's some example that I use:
NavigationHostActivity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
this.bind(
this.findNavController(R.id.navigation_host_fragment),
this.binding.navigationBottomView
)
}
fun bind(navigationController: NavController, bottomNavigationView: BottomNavigationView) {
navigationController.addOnDestinationChangedListener { _, destination, _ ->
this.hideBottomNavigation(bottomNavigationView)
bottomNavigationView.menu.forEach {
if (it.itemId == destination.id) {
this.showBottomNavigation(bottomNavigationView)
}
}
}
}
fun hideBottomNavigation(bottomNavigationView: BottomNavigationView) { bottomNavigationView.visibility = View.GONE }
fun showBottomNavigation(bottomNavigationView: BottomNavigationView) { bottomNavigationView.visibility = View.VISIBLE }
This will check wether the destination of Navigation is the same as menu of your BottomNavigation. If it's one of the BottomNavigation Menu, then it Show the BottomNavigation.
Actually I put bind
, show
and hide
function in NavigationService, so this is roughly what I did. And this was made for easier example. Feel free to tweak it. I also use DataBinding here FYI.
navigation_host_fragment
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/navigation_bottom_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFF"
app:itemIconSize="22dp"
app:itemIconTint="@color/state_bottom_navigation_view"
app:itemTextColor="@color/state_bottom_navigation_view"
app:labelVisibilityMode="labeled"
app:layout_constraintBottom_toBottomOf="parent"
app:menu="@menu/menu_dashboard" />
<fragment
android:id="@+id/navigation_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@id/navigation_bottom_view"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/navigation_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
<data>
<variable
name="activity"
type="*.view.NavigationHostActivity" />
</data>
</layout>
menu_dashboard
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/dashboardFragment"
android:title="Dashboard"/>
<item
android:id="@+id/searchFragment"
android:title="Search" />
<item
android:id="@+id/profileFragment"
android:title="Profile" />
</menu>
And that's it, work perfectly for my case. Hope it helps :D
Regards,
Andrea
Upvotes: 3
Reputation: 6522
It's tricky with activities, but you can achieve the same effect in single-activity application by using children fragments.
FragmentA
and FragmentB
. Move all UI from ActivityA
to FragmentA
and from ActivityB
to FragmentB
. ActivityA
should hold a NavHostFragment
only. Remove ActivityB
.FragmentA
, FragmentB
and connect them. Install the graph to ActivityA
.FragmentA1
, FragmentA2
, FragmentA3
and install it to NavHostFragment
declared in FragmentA
.FragmentB1
and FragmentB2
and install it to NavHostFragment
declared in FragmentB
.That's it. Now when you need to navigate between FragmentA
and FragmentB
or back, just use navigation controller from parent fragment: parentFragment?.findNavController()
or view model from parent fragment (if you are using viewmodels for navigation).
Upvotes: 0