Prakash
Prakash

Reputation: 7996

How to navigate between two activities using jetpack navigation library

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>

Navigation image for reference

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

Answers (3)

straya
straya

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

Andrea Lk
Andrea Lk

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.

Some workaround

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:

  1. NavigationHostActivity (Activity that has BottomNavigation)
  2. DashboardFragment (Fragment)
  3. SearchFragment (Fragment)
  4. ProfileFragment (Fragment)
  5. ProfileDetailFragment (Fragment)

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

esentsov
esentsov

Reputation: 6522

It's tricky with activities, but you can achieve the same effect in single-activity application by using children fragments.

  1. Create two fragments, FragmentA and FragmentB. Move all UI from ActivityA to FragmentA and from ActivityB to FragmentB. ActivityA should hold a NavHostFragment only. Remove ActivityB.
  2. Create main navigation graph containing FragmentA, FragmentB and connect them. Install the graph to ActivityA.
  3. Create another navigation graph, containing FragmentA1, FragmentA2, FragmentA3 and install it to NavHostFragment declared in FragmentA.
  4. Create one more navigation graph with 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

Related Questions