android - 拦截Fragment中的ActionBar Home按钮

标签 android android-fragments android-actionbar android-fragmentactivity android-appcompat

我可以从我的 NavigationDrawerFragment 中成功拦截 ActionBar 主页按钮,它被添加到我的 MainActivity 中,如下所示:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if (!loggedIn() && item.getItemId() == android.R.id.home) {
        login();
        return true;
    }
    return super.onOptionsItemSelected(item);
}

但是,在我的带有 ComposeFragmentComposeActivity 中,这不起作用。 onOptionsItemSelected 未在 fragment 上调用。

我已经调试了代码,问题似乎归结为 Android 支持库的设计。看来 FragmentActivityActivity 都有自己对 FragmentManager 的引用。

FragmentActivity 首先检查 Activity 是否可以处理事件,然后再检查其任何 fragment ,这与 docs 一致:

/**
 * Dispatch context and options menu to fragments.
 */
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
    if (super.onMenuItemSelected(featureId, item)) {
        return true;
    }

    switch (featureId) {
        case Window.FEATURE_OPTIONS_PANEL:
            return mFragments.dispatchOptionsItemSelected(item);

        case Window.FEATURE_CONTEXT_MENU:
            return mFragments.dispatchContextItemSelected(item);

        default:
            return false;
    }
}

如以下代码 fragment 所示,Activity 在检查它或其任何 fragment 是否可以处理该事件后,将处理主页按钮作为最后的手段。但是这个对 FragmentManager 的引用不包含任何 fragment , fragment 在 FragmentActivity 的管理器中。因此,如果设置了 ActionBar.DISPLAY_HOME_AS_UP,该事件将被 Activity 类吞没。

public boolean onMenuItemSelected(int featureId, MenuItem item) {
    /* ... */
    if (onOptionsItemSelected(item)) {
        return true;
    }
    if (mFragments.dispatchOptionsItemSelected(item)) {
        return true;
    }
    if (item.getItemId() == android.R.id.home && mActionBar != null &&
            (mActionBar.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
        if (mParent == null) {
            return onNavigateUp();
        } else {
            return mParent.onNavigateUpFromChild(this);
        }
    }
    return false;
    /* ... */
}

事实证明,我的 MainActivity 作为我的应用程序的根并使用抽屉导航,没有设置此标志,因此没有吞下该事件。但是我的 ComposeActivity 有一个父 Activity ,我需要设置这个标志,这样就不可能拦截操作栏主页按钮。

To sum up the issue: It is not possible to intercept a click on the Action Bar home button from a fragment in an activity with with DISPLAY_HOME_AS_UP set.

那么这是支持库中的错误吗?如果我针对更高版本的 Android 并删除支持库,它看起来不会发生。

关于解决方法,我想我可以:

  • 在我的 ComposeActivityonOptionsItemSelected 中,我可以手动将事件传递到我的每个 fragment ,看看它们是否可以在调用 super 之前处理它。
  • 重写 ComposeActivity 中的 onMenuItemSelected 并做同样的事情。

有人遇到过这个吗?我应该在某处记录错误吗?关于解决此问题的任何其他想法?

最佳答案

正如您所解释的,由于事件在 Android 中的调度流程,子 fragment 似乎永远不会拦截事件,因为它首先被 Activity 使用。

这是一种变通方法,但我正在做的是在 Activity 处理之前将事件传递给子 fragment 。

在 Activity 中:

    @Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Due to a problem of not being able to intercept android.R.id.home in fragments,
    // we start passing the event to the currently displayed fragment.
    // REF: http://stackoverflow.com/questions/21938419/intercepting-actionbar-home-button-in-fragment
    final Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.XXXXXXX);
    if (currentFragment != null && currentFragment.onOptionsItemSelected(item)) {
        return true;
    }

    switch (item.getItemId()) {
        case XXX:
            ...
            return true;
        case YYY:
            ...
            return true;
        default:
            break;
    }
    return super.onOptionsItemSelected(item);
}

关于android - 拦截Fragment中的ActionBar Home按钮,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21938419/

相关文章:

android - 带 fragment 的底部导航栏

android - 在 PopBackStack 上保留数据

android - 更改支持 ActionBarDrawerToggle 图标

API 19 的 Android 工具栏? (API 21 工作正常)

android - 单击 RecyclerView 项目后打开对话框?

android - 自定义日历 View (回历)

android - aab 格式在 32 位设备下返回没有 armeabi_v7a 库的 apk

android - 将 Parcelable Arraylist 发送到 fragment

android - 具有替代显示的 ActionBar 列表导航

java - 在多个 fragment 中显示日期