18

I have created an Androidx project with Navigation pattern. Now I want to close drawerLayout onBackPressed() in fragment. And also I want to set OnClickListener to drawer menu items to my custom menu there is no way I guess to override the onBackPressed() in fragment.

I want to know how I can achieve this?

Here is my drawer:

<androidx.drawerlayout.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:id="@+id/drawerLayoutHome"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="350dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="@color/colorPrimaryDark"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header_main"
        app:itemIconTint="@color/white"
        app:itemTextColor="@color/white"
        app:menu="@menu/activity_main_drawer"/>

</androidx.drawerlayout.widget.DrawerLayout>

I want to achieve this:

override fun onBackPressed() {

    if (drawerLayoutHome.isDrawerOpen(GravityCompat.END)) {
        drawerLayoutHome.closeDrawer(GravityCompat.END)
    } else {
        super.onBackPressed()
    }
}

But this is for Activity, how to achieve this in Fragment?

Arbaz Pirwani
  • 935
  • 7
  • 22

6 Answers6

8

Hello there you can do it by getting your fragment in activity and override onbackpress with kotlin code

This is your backpress function and in this you can get your nav host and then you can check your current fragment or the fragment you want to open drawer in .

override fun onBackPressed() {

    val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragmentHomeHost)
    val completeFragmentList = navHostFragment?.childFragmentManager?.fragments

    if (completeFragmentList != null) {
        when {
            completeFragmentList[0] is HomeFragment -> {
                val home = (completeFragmentList[0] as HomeFragment)


            }
            else -> super.onBackPressed()
        }
    }

    if (drawerLayoutHome.isDrawerOpen(GravityCompat.START)) {
        drawerLayoutHome.closeDrawer(GravityCompat.START)
    } else {
        super.onBackPressed()
    }
}

And here is hoe you can get selection item listener

override fun onNavigationItemSelected(item: MenuItem): Boolean {
    drawerLayoutHome.closeDrawer(GravityCompat.START)

    return true
}
Hello world
  • 80
  • 1
  • 10
4

You can do that within a Fragment by implementing OnBackStackChangedListener on your drawer activity.
Then, link your drawer with ActionBarDrawerToggle and override onBackStackChanged() and synchronize your drawer toggle.

Here is the full snippet:

public class MainActivity extends AppCompatActivity implements FragmentManager.OnBackStackChangedListener {


    private DrawerLayout drawer;
    private ActionBarDrawerToggle toggle;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //Bind listener here
        getSupportFragmentManager().addOnBackStackChangedListener(this);
        drawer = findViewById(R.id.drawer_layout);
        toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        toggle.setToolbarNavigationClickListener(view -> onBackPressed());
        drawer.addDrawerListener(toggle);
        toggle.syncState();

    }


    //Override fragment backstack lister method 
    @Override
    public void onBackStackChanged() {
        //toggle.setDrawerIndicatorEnabled(getSupportFragmentManager().getBackStackEntryCount() == 0);
        //getSupportActionBar().setDisplayHomeAsUpEnabled(getSupportFragmentManager().getBackStackEntryCount() > 0);
        toggle.syncState();
    }

    @Override
    public void onBackPressed() {
        drawer = findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START))
            drawer.closeDrawer(GravityCompat.START);
        else {
            //super.onBackPressed();
        }
    }
}

I hope this will help you.

Davide Cannizzo
  • 2,826
  • 1
  • 29
  • 31
Mehul Solanki
  • 1,147
  • 11
  • 15
  • My drawer is in my activity – Arbaz Pirwani May 10 '19 at 10:11
  • I have implemented onback press in activity. But I was asking to handle it in directly fragment not want to write code in activity – Arbaz Pirwani May 10 '19 at 13:05
  • 1
    Here are two different methods, `onBackPressed()` for activity and `onBackStackChanged()` for fragment with `FragmentManager.OnBackStackChangedListener` on activty so when fragment is visible and back action triggers at that time `onBackStackChanged()` called. and inside `onBackStackChanged()` we syncing the drawer taggle `toggle.syncState()` so if drawer is open so drawer going to be close. – Mehul Solanki May 13 '19 at 09:37
  • Hey this answer need explanation in kotlin language and you are providing in java – Hello world May 15 '19 at 05:10
  • As per android doc, java and kotlin working together very smoothly and another thing are you can easily convert your any java code to kotlin in android studio. – Mehul Solanki May 15 '19 at 05:37
  • And the second part of the question is menu click listener i have added in my answer please if you can review it thanks – Hello world May 15 '19 at 05:46
2

In Kotlin, simply override this method in your activity,

override fun onBackPressed() {
    if (drawer_layout.isDrawerOpen(GravityCompat.START))
        drawer_layout.closeDrawer(GravityCompat.START)
    else
        super.onBackPressed()
}
Davide Cannizzo
  • 2,826
  • 1
  • 29
  • 31
Zeeshan Ayaz
  • 858
  • 6
  • 11
  • You are right i have done this but i want it in fragment – Arbaz Pirwani May 08 '19 at 18:53
  • A fragment always contains a base activity. You can have your navigation drawer in that activity....... – Zeeshan Ayaz May 09 '19 at 08:30
  • what the benefit of using it in fragment? for example: if you use it in a 1st Fragment, and by navigating to another fragment through any item from navigation drawer, it will navigate to another fragment, and you have to create navigation drawer to that fragment as well to navigate again to any other ..... and soon.... – Zeeshan Ayaz May 10 '19 at 17:42
  • My navigation drawer will only be available in one fragment – Arbaz Pirwani May 10 '19 at 17:50
  • then, try to create custom toolbar in fragment and make your activity theme as noActionBar. Hopefully it works.... – Zeeshan Ayaz May 11 '19 at 07:15
2

Short answer

It's not possible to intercept onBackPressed callback inside Fragment without adding additional code inside hosting Activity. It's simply not implemented in Android SDK. Just stop.

Original answer

There is no such thing as onBackPressed callback in Fragment class. But you can implement it yourself:

Create an interface:

interface FragmentOnBackPressedListener {
  fun onBackPressed()
}

Now in your activity:

override fun onBackPressed() {
  // find top fragment
  val currentFragment = supportFragmentManager
    .findFragmentById(R.id.your_nav_host_fragment_id)?.let {
      it.childFragmentManager.fragments.getOrNull(0)
    }
  // check if it implements FragmentOnBackPressedListener 
  if (currentFragment is FragmentOnBackPressedListener ) {
    // if it does, transfer flow to the fragment
    return currentFragment.onBackPressed()
  }
  // if it doesn't, apply default behaviour
  super.onBackPressed()
}

Then in your fragment:

class ExampleFragment : Fragment(), FragmentOnBackPressedListener {

  override fun onBackPressed() {
    // close your drawer layout here if visible

    // Also: to close fragment, invoke 
    // findNavController().navigateUp() or findNavController().popBackStack()
  }

}
Community
  • 1
  • 1
xinaiz
  • 7,744
  • 6
  • 34
  • 78
1

You can control onBackPress() from within fragments with OnBackPressedDispatcher.

class SampleFragment: Fragment() {
 
    override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
        
        requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true){
            override fun handleOnBackPressed() {
                if (drawer.isDrawerOpen(GravityCompat.START)) {
                    drawer.closeDrawer(GravityCompat.START)
                } else{
                    isEnabled = false
                    requireActivity().onBackPressed() }}
                }
            })
        }
 }
tfad334
  • 558
  • 6
  • 14
0

I have had a similar problem.

So, I added this in onBackPressed of my Activity file.

FragmentManager manager = getSupportFragmentManager();
 if (manager.getBackStackEntryCount() > 0) {
                while (manager.getBackStackEntryCount() > 0) {
                    manager.popBackStackImmediate();
                }
sanjeev
  • 1,664
  • 19
  • 35