Search code examples
androidkotlinandroid-fragmentsandroid-actionbarandroid-navigation

How to go to the previous fragment by back button in action bar? (Kotlin)


I have two fragments and I want to create interaction between them using backward button in action bar. Ideally, I would like the state of previous fragment was saved. I could find information for only activities.

For fragments I found this

private fun setupBackButton() {
        if (activity is AppCompatActivity) {
            (activity as AppCompatActivity?)?.supportActionBar?.setDisplayHomeAsUpEnabled(true)
        }
    }

But it only displays back button, nothing happens by clicking.

EDIT

In the first fragment I call the second like:

val fragment = UserContentFragment()
fragment.setUser(item.user)

if (fragmentManager != null) {
    fragmentManager!!
      .beginTransaction()
      .replace(R.id.main_layout, fragment)
      .addToBackStack(null)
      .commit()
}

This is my UserContentFragment second fragment:

class UserContentFragment : Fragment() {

    private lateinit var user: SearchUser

    fun setUser(user: SearchUser) {
        this.user = user
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val root = inflater.inflate(R.layout.fragment_user_content, container, false)

        val userImage = root.findViewById(R.id.user_img) as ImageView
        if (context != null) {
            Glide.with(context!!)
                .load(user.profile_pic_url)
                .circleCrop()
                .into(userImage)
        }

        val userName: TextView = root.findViewById(R.id.user_name)
        userName.text = user.full_name

        val toolbar: Toolbar = root.findViewById(R.id.toolbar)
        toolbar.setNavigationOnClickListener { requireActivity().onBackPressed() }

        setupBackButton()
        
        return root
    }

    private fun setupBackButton() {
        if (activity is AppCompatActivity) {
            (activity as AppCompatActivity?)?.supportActionBar?.setDisplayHomeAsUpEnabled(true)
        }
    }
}

And this its .xml file:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:background="@color/colorBlack">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/user_title"
        android:layout_width="match_parent"
        android:layout_height="100dp">

        <ImageView
            android:id="@+id/user_img"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:layout_marginStart="16dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:ignore="ContentDescription" />

        <TextView
            android:id="@+id/user_name"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="32dp"
            android:layout_marginEnd="16dp"
            android:textColor="@color/colorWhite"
            android:textSize="22sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@+id/user_img"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</RelativeLayout>

Solution

  • In order to have a response when you hit the Home/up Button, here are a couple of options to solve this:

    First Option

    In the fragments that you show up the Home/UP button, override onOptionsItemSelected() method and call the activity's onBackPressed() for home button id

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        // Handle presses on the action bar menu items
        when (item.itemId) {
            android.R.id.home -> {
                activity?.onBackPressed()
                return true
            }
        }
        return super.onOptionsItemSelected(item)
    }
    

    Side note:

    Instead of showing the Home/Up button on the ActionBar using below, and you've to set the boolean value for each fragment that you need to show up the home button using the method below:

    private fun setupBackButton() {
        if (activity is AppCompatActivity) {
            (activity as AppCompatActivity?)?.supportActionBar?.setDisplayHomeAsUpEnabled(true)
        }
    }
    

    You can instead setup the ActionBar with AppBarConfiguration in onCreate() method of the activity as below:

    private lateinit var appBarConfiguration: AppBarConfiguration
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
        val host: NavHostFragment = supportFragmentManager
                .findFragmentById(R.id.my_nav_host_fragment) as NavHostFragment? ?: return
    
        val navController = host.navController
    
        appBarConfiguration = AppBarConfiguration(
                setOf(R.id.second_fragment, R.id.third_fragment)) //  IDs of fragments you want without the ActionBar home/up button
    
        setupActionBarWithNavController(navController, appBarConfiguration)
    
    }
    

    By doing this, the up button will show up in all fragments, but R.id.second_fragment, R.id.third_fragment and you no longer need to set the setDisplayHomeAsUpEnabled() to each individual fragment to show up the home/up button. But still you need to override onOptionsItemSelected as mentioned above.

    Second Option

    Which is neater than the first option. First, you've to implement the above side node to allow the NavController auto controls/configure the ActionBar.

    So, the past side note is a mandatory part of this option.

    Then override onSupportNavigateUp() in the activity which allows NavigationUI to support proper ActionBar up navigation.

    override fun onSupportNavigateUp(): Boolean {
        return findNavController(R.id.my_nav_host_fragment).navigateUp(appBarConfiguration)
    }
    

    And then override onOptionsItemSelected() in activity to make Have Navigation UI Handle the OptionsMenu/ActionBar item selection

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return item.onNavDestinationSelected(findNavController(R.id.my_nav_host_fragment)) 
                || super.onOptionsItemSelected(item)
    }
    

    I would say that option 2 is neater than 1, because you write all the code in one place (activity) without touching fragments, and also it automatically configure all the fragments, no need to manually setDisplayHomeAsUpEnabled() or activity.onBackPressed() for individual fragments that you want the Home/Up button to show up.