Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Default Behavior for OnBackPressedCallback in Activity?

I would like to implement the OnBackPressedCallback detailed in Navigation Components in my app. The documentation is very clear on how to add this custom behavior in fragments, and works quite well. After reading the linked documentation, it states that you should avoid overriding onBackPressed in your activity. My current onBackPressed method looks something like this:

@Override
public void onBackPressed() {

    if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
      drawerLayout.closeDrawer(GravityCompat.START);
    } else if (shouldOverrideBackPressed()) {
      navigationController.popBackStack(R.id.main_fragment, false);
    } else {
      super.onBackPressed();
    }
}

The last line super.onBackPressed(); is what I'm not clear about. How do I retain the default behavior for the back button in an activity while implementing the OnBackPressedCallback? This is my current implementation:

getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
  @Override
  public void handleOnBackPressed() {
    if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
      drawerLayout.closeDrawer(GravityCompat.START);
    } else if (shouldOverrideBackPressed()) {
      navigationController.popBackStack(R.id.main_fragment, false);
    } else {
      setEnabled(false);
      MainActivity.this.onBackPressed();
    }
  }
});

If I don't include the setEnabled(false); line, then I get a Stack Overflow error, which makes sense. But surely there is a more elegant way to provide default behavior?

like image 398
Elli White Avatar asked Feb 26 '26 14:02

Elli White


1 Answers

You shouldn't be putting all of that logic in one OnBackPressedCallback. Instead, each case should be its own callback that is only enabled when it is the one that should be handling the back pressed.

You should not be calling onBackPressed(), etc in handleOnBackPressed(). An important part of the contract is that when you get a callback to handleOnBackPressed(), you must handle the back pressed button there. That's precisely why you have control over exactly when the callback is enabled or not.

This means you should create one OnBackPressedCallback for your override behavior (although you should probably not being doing that with Navigation anyways) and another for the DrawerLayout that uses a DrawerListener for enabling and disabling the callback:

OnBackPressedDispatcher dispatcher = getOnBackPressedDispatcher();

final OnBackPressedCallback overrideCallback = new OnBackPressedCallback(false) {
    @Override
    public void handleOnBackPressed() {
        navigationController.popBackStack(R.id.main_fragment, false);
    }
};
// Whenever your `shouldOverrideBackPressed()` changes, you need to
// call setEnabled(true) or setEnabled(false) so that callback
// is only called exactly when it needs to handle the back button

// Add this one first since they are called in reverse order
dispatcher.addCallback(this, overrideCallback);

// Now set up the DrawerLayout's callback
final DrawerLayout drawerLayout = findViewById(R.id.your_drawer_layout);
final OnBackPressedCallback drawerCallback = new OnBackPressedCallback(
        drawerLayout.isDrawerOpen(GravityCompat.START)) {
    @Override
    public void handleOnBackPressed() {
        // Unconditionally close the drawer when it is open
        drawerLayout.closeDrawer(GravityCompat.START);
    }
};
// Now add a listener so that this callback is only
// enabled when the drawer is open
drawerLayout.addDrawerListener(new DrawerLayout.SimpleDrawerListener() {
    @Override
    public void onDrawerClosed(View drawerView) {
        // Assume you only have one drawer
        drawerCallback.setEnabled(false);
    }

    @Override
    public void onDrawerOpened(View drawerView) {
        // Assume you only have one drawer
        drawerCallback.setEnabled(true);
    }
});
// Add it last so that it gets called before the overrideCallback
dispatcher.addCallback(this, drawerCallback);

Of course, if you're overriding things at the Activity level, there's no downside to keeping your logic in onBackPressed() - as per the documentation, any callbacks registered from Fragments, etc. will correctly be called when you call super.onBackPressed().

like image 190
ianhanniballake Avatar answered Feb 28 '26 07:02

ianhanniballake



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!