2

I just set up a projet with Android navigation component and here's the structure of my graph:

<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/nav_graph"
    app:startDestination="@id/a">


    <fragment
        android:id="@+id/a"
        android:name="com.example.tutorial.fragmentA"
        android:label="a">

        <action android:id="@+id/action_a_to_b"
            app:destination="@id/b"
            app:enterAnim="@anim/nav_default_enter_anim"
            app:exitAnim="@anim/nav_default_exit_anim"
            app:popEnterAnim="@anim/nav_default_pop_enter_anim"
            app:popExitAnim="@anim/nav_default_pop_exit_anim"/>

    </fragment>

    <fragment
        android:id="@+id/b"
        android:name="com.example.tutorial.fragmentB"
        android:label="b">

    </fragment>

</navigation>

In fragment A I navigate to B like this:

 findNavController().navigate(R.id.action_a_to_b)

In fragment B, I have a custom toolbar and the idea is that a click on the top left arrow should close fragment B and resume A:

(activity as AppCompatActivity).setSupportActionBar(binding.toolbar)

 binding.toolbar.setNavigationOnClickListener {
        findNavController().popBackStack()
    }

Same goes if I click on the Key down press button:

       requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner){
        findNavController().popBackStack()
    }

The problem I'm still stuck in fragment B: the exit animation starts and ends and I'm still in fragment B. Any ideas how to go about to fix this ?

Claude Hangui
  • 426
  • 3
  • 8
  • 29

3 Answers3

0

I've created a small project with 2 fragments and it works flowless. We have a login Screen and a screen with passwords.

LoginFragment

class LoginFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_login, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val toolbar = view.findViewById<androidx.appcompat.widget.Toolbar>(R.id.loginToolbar)
        toolbar.navigationIcon = null
        // Set the Toolbar as your activity's ActionBar
        (requireActivity() as AppCompatActivity).setSupportActionBar(toolbar)

        // Find this Fragment's NavController
        val navController = NavHostFragment.findNavController(this);

        // And set up the ActionBar
        NavigationUI.setupActionBarWithNavController(
            requireActivity() as AppCompatActivity,
            navController
        )

        val b = view.findViewById<MaterialButton>(R.id.loginButton)
        b.setOnClickListener {
            this.findNavController().navigate(R.id.passwordsFragment)
        }
    }
}

PasswordFragment

class PasswordsFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_passwords, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val toolbar = view.findViewById<androidx.appcompat.widget.Toolbar>(R.id.myToolbar)
        // Set the Toolbar as your activity's ActionBar
        (requireActivity() as AppCompatActivity).setSupportActionBar(toolbar)

        // Find this Fragment's NavController
        val navController = NavHostFragment.findNavController(this);

        // And set up the ActionBar
        NavigationUI.setupActionBarWithNavController(
            requireActivity() as AppCompatActivity,
            navController
        )
        toolbar.setNavigationOnClickListener {
            navController.navigateUp()
        }
    }
}

Toolbar in the LoginFragment XML

  <androidx.appcompat.widget.Toolbar
        android:id="@+id/loginToolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/black"
        app:layout_constraintTop_toTopOf="parent" />

Toolbar in the PasswordFragment XML

<androidx.appcompat.widget.Toolbar
    android:id="@+id/myToolbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/black"
    app:layout_constraintTop_toTopOf="parent" />

MainActivity 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/constraintLayout3"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:elevation="6dp"
    android:fillViewport="true"
    android:orientation="vertical">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/my_nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/nav_graph" />

</androidx.constraintlayout.widget.ConstraintLayout>

I navigate from the loginFragment to the PasswordFragment and then I press the up button and I get to the previous fragment :)

Let me know if it works

Pako1
  • 247
  • 2
  • 12
  • thanks, but this works only if you've got a default action bar for all the fragments which is therefore managed by the hosting activity. Each of the fragments hosted in my activity have their own action bars – Claude Hangui Feb 04 '22 at 00:23
  • Hmm.. Btw do you call setSupportActionBar(toolbar) before calling toolbar.setNavigationOnClickListener() ? – Pako1 Feb 04 '22 at 10:40
  • I did not call `setSupportActionBar(toolbar)` in my fragment before `toolbar.setNavigationOnClickListener()` but the click is handled cuz I just added a Toast there and it's actually displayed Irrespective of that, there's still the bottom key that has the same problem: clicking on it doesn't resume the previous fragment – Claude Hangui Feb 04 '22 at 11:08
  • Update: added `(activity as AppCompatActivity).setSupportActionBar(binding.toolbar)` as the 1st line in my `onViewCreated()` for fragment B before making a call to `setNavigationOnClickListener()`, the result is still the same – Claude Hangui Feb 04 '22 at 11:18
  • I'm working inside fragments, and not in the hosting activity – Claude Hangui Feb 04 '22 at 11:57
  • Sorry I copied the wrong code: As from the documentation it says: The navigation configuration logic is the same for both of these fragments, except that you should call setupWithNavController() from within each fragment's onViewCreated() method, instead of initializing them from the activity: val navController = findNavController() val appBarConfiguration = AppBarConfiguration(navController.graph) view.findViewById(R.id.toolbar) .setupWithNavController(navController, appBarConfiguration) – Pako1 Feb 04 '22 at 12:24
  • Tried it, my left arrow disappeared – Claude Hangui Feb 04 '22 at 13:06
  • Updated the answer above :) – Pako1 Feb 04 '22 at 14:21
  • I tried your approach: created a basic project and set up everything like you except for the toolbar where I added a custom arrow. The arrow isn't still visible; perhaps you could have a try on your side: you could just add this line in the toolbar `app:navigationIcon="@drawable/ic_back_arrow"` – Claude Hangui Feb 04 '22 at 15:33
  • Its not havng an icon because I have set toolbar.navigationIcon = null in the loginFragment. because there is no previous fragment from that point. You might want to delete that one line. – Pako1 Feb 04 '22 at 15:36
  • I am in the `PasswordFragment`: I believe it doesn't have has anything to do with the toolbar in the `LoginFragment` yes ?! If that's the case perhaps you could add an icon in the Toolbar for PasswordFragment; you'll see it in the editor but upon building the app it's not going to be displayed – Claude Hangui Feb 04 '22 at 15:48
  • Looks like calling `setupActionBarWithNavController()` will hide the back/up arrow key. I removed it and I'm able to see the arrow and clicking on it does brings back to the `LoginFragment`. I don't really know what's going on with the project on which I'm working on but I'll investigate the matter. Thanks for the help – Claude Hangui Feb 04 '22 at 15:59
  • Nope it does not have to do with the PasswordFragment. I've run the code on a device and I got it work properly. Are you using maybe in your theme – Pako1 Feb 04 '22 at 16:04
0
private lateinit var appBarConfiguration: AppBarConfiguration
//onCreate
val navHostFragment =
        supportFragmentManager.findFragmentById(R.id.fragment4) as NavHostFragment
    val navController = navHostFragment.navController
    appBarConfiguration = AppBarConfiguration(navController.graph, drawerLayout)
    setupActionBarWithNavController(navController, appBarConfiguration)
    navDrawView.setupWithNavController(navController)
    bottomAppBar.background = null
    bottomAppBar.setupWithNavController(navController)
//automatic NavigateUp setup
override fun onSupportNavigateUp(): Boolean {
    val navController = findNavController(R.id.fragment4)
    return navController.navigateUp(appBarConfiguration)
            || super.onSupportNavigateUp()
}

Can you try this setup in your main activity, it's worked for me I use bottomNav navdrawer and appbar together.

if you wanna use custom Toolbar

//in fragment or activity
private fun handleToolbarChanges() {
    with(binding.toolbarDataAnalysis) {
        setNavigationOnClickListener { onBackPressed() }
        title = "your title here"
    }
}
//in xml file
<androidx.appcompat.widget.Toolbar
    android:id="@+id/toolbarDataAnalysis"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/green"
    app:navigationIcon="@drawable/ic_baseline_arrow_back_24"
    app:titleTextColor="@color/white" />
0

Now its possible we can create observer for return values from second fragment using popBackStack()

Suppose we have firstFragment and secondFragment

In firstFragment use this for observer :-

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    val navController = findNavController()
    navController.currentBackStackEntry?.savedStateHandle?.getLiveData<String>("isDelete")
        ?.observe(viewLifecycleOwner) {
            if(it.lowercase().compareTo("true")==0)
            {
                //write code here
            }
            else
            {
                //write code here
            }
        }
}

In secondFragment use this code

val navController = findNavController()
    navController.previousBackStackEntry?.savedStateHandle?.set("isDelete", "true/false")
    navController.popBackStack()
Hamza Rasheed
  • 179
  • 1
  • 2