活动中OnBackPressedCallback的默认行为?

时间:2019-12-03 00:06:41

标签: android android-jetpack android-architecture-navigation

我想在我的应用中实现导航组件中详细介绍的OnBackPressedCallback。该文档非常清楚如何在片段中添加此自定义行为,并且效果很好。阅读链接的文档后,它指出您应该避免在活动中覆盖onBackPressed。我当前的onBackPressed方法看起来像这样:

@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();
    }
}

最后一行super.onBackPressed();是我不清楚的。在实施OnBackPressedCallback时,如何在活动中保留后退按钮的默认行为?这是我当前的实现:

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();
    }
  }
});

如果我不包括setEnabled(false);行,那么我会得到一个堆栈溢出错误,这是有道理的。但是,肯定有一种更优雅的方式来提供默认行为吗?

1 个答案:

答案 0 :(得分:1)

您不应该将所有这些逻辑都放在一个OnBackPressedCallback中。相反,每种情况都应为其自己的 own 回调,仅当该回调应处理后退时才启用。

您不应onBackPressed()中呼叫handleOnBackPressed()等。合同的重要部分是,当您收到对handleOnBackPressed()的回调时,必须在此处处理向后按下的按钮。这就是为什么您可以完全控制何时启用回调的原因。

这意味着您应该为您的替代行为创建一个OnBackPressedCallback(尽管您可能无论如何都不应该通过导航来创建),并为使用DrawerListenerDrawerLayout创建另一个并禁用回调:

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);

当然,如果您要在“活动”级别覆盖某些内容,那么将逻辑保持在onBackPressed()中就没有任何弊端-根据the documentation,从Fragments等注册的任何回调都将正确存在致电super.onBackPressed()时被呼叫。