maksonnie
maksonnie

Reputation: 743

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>

Upvotes: 4

Views: 16737

Answers (7)

Vitor Lira
Vitor Lira

Reputation: 1

It's a late answer but I hope it will help someone.

I did it in Kotlin and the way I managed to make it work is:

In the MainActivity I overrode the onSupportNavigateUp:

override fun onSupportNavigateUp(): Boolean {
        val navController = findNavController(R.id.nav_host_fragment_activity_main)
        return navController.navigateUp(appBarConfiguration)
                || super.onSupportNavigateUp()

}

And declared the AppBarConfiguration outside the onCreate()

Upvotes: 0

Adam Giergun
Adam Giergun

Reputation: 51

Zain's Second Option is very good, but I'll show a different way for the part made in onCreate, with usage of DataBinding:

ActivityMainBinding.inflate(layoutInflater).run {
            
    setContentView(root)

    setSupportActionBar(toolbar)

    (supportFragmentManager.findFragmentById(R.id.my_nav_host_fragment)
        as NavHostFragment).navController.let { navController ->

            val appBarConfiguration = AppBarConfiguration(navController.graph)

            toolbar.setupWithNavController(navController, appBarConfiguration)
    }
}

Upvotes: 0

Induwara Rajapaksha
Induwara Rajapaksha

Reputation: 71

In Kotlin Fragment with Navigation.

First, you add setHasOptionsMenu(true) in onCreate

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setHasOptionsMenu(true)
}

Then override the onOptionsItemSelected and when R.id.home you can control BACK Button

override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.getItemId()) {
            android.R.id.home ->
                findNavController().navigate(R.id.action_FragmentTwo_to_FragmentOne)
        }
        return true
}

Upvotes: 4

Waqas Ali
Waqas Ali

Reputation: 51

class StartActivity : FragmentActivity() {
    /**
     * The pager widget, which handles animation and allows swiping horizontally to access previous
     * and next wizard steps.
     */
    private lateinit var mPager: ViewPager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.start_activity)

        val loginButton = findViewById<Button>(R.id.login_button)
        loginButton.setOnClickListener {
            this.didTapLoginButton()
        }
    }

    private fun didTapLoginButton() {
        val i = Intent(this, LoginActivity::class.java)
        startActivity(i)
    }
}

class LoginActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.login_activity)

        //actionbar
        val actionbar = supportActionBar
        //set actionbar title
        actionbar!!.title = "New Activity"
        //set back button
        actionbar.setDisplayHomeAsUpEnabled(true)
        actionbar.setDisplayHomeAsUpEnabled(true)
    }

    override fun onSupportNavigateUp(): Boolean {
        onBackPressed()
        return true
    }
    // it is important function, you need to write this function in which class/activity you want to show back arrow
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return super.onOptionsItemSelected(item)
    }
}

Upvotes: 1

Zain
Zain

Reputation: 40840

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.

Upvotes: 6

Kaita John
Kaita John

Reputation: 1109

Add this in the NavigationClickListener.

FragmentManager fm = getFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
    Log.i("MainActivity", "popping backstack");
    fm.popBackStack();
} else {
    Log.i("MainActivity", "nothing on backstack, calling super");
    super.onBackPressed();  
}

Upvotes: 0

EraftYps
EraftYps

Reputation: 818

You need to attach a click listener to the toolbar like :

toolbar.setNavigationOnClickListener { requireActivity().onBackPressed() }

Upvotes: 1

Related Questions