Activity 中 OnBackPressedCallback 的默认行为?

Ell*_*ite 10 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

最后一行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();
    }
  }
});
Run Code Online (Sandbox Code Playgroud)

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

ian*_*ake 10

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

应该在 中打电话onBackPressed()handleOnBackPressed()。合约的一个重要部分是,当您收到 的回调时handleOnBackPressed(),您必须处理那里按下的后退按钮。这正是您可以准确控制回调何时启用或不启用的原因。

这意味着您应该OnBackPressedCallback为您的覆盖行为创建一个(尽管无论如何您可能不应该使用导航来执行此操作),并为DrawerLayout使用 aDrawerListener来启用和禁用回调的行为创建另一个:

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);
Run Code Online (Sandbox Code Playgroud)

当然,如果您在 Activity 级别覆盖某些内容,那么保留逻辑并没有什么坏处onBackPressed()- 根据文档,从 Fragments 等注册的任何回调在您调用时都将正确调用super.onBackPressed()

  • 以声明方式启用/禁用回调看起来不错,但有时无法“委托给默认/下一个”非常不方便。例如,使用 `WebView`:`if (webView.canGoBack()) webView.goBack() else ???` 或使用自定义导航:`if (!viewModel.goBack()) ???`。这里的“viewModel”应该完全控制导航,除了退出应用程序。在这种特定情况下,我们可以调用“finish()”,但[不推荐](https://developer.android.com/about/versions/12/behavior-changes-all#back-press)。在这些情况下,“返回 false”(例如触摸事件)可能会更好。 (2认同)