在Fragment中拦截ActionBar的Home按钮

5

我可以成功地从我的NavigationDrawerFragment中拦截ActionBar的主页按钮,这个NavigationDrawerFragment被添加到我的MainActivity中,操作如下:

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

然而,在我的ComposeActivityComposeFragment中,这种方法不起作用。在片段上不会调用onOptionsItemSelected
我已经调试了代码,问题似乎归结于Android支持库的设计。看起来FragmentActivityActivity都有自己对FragmentManager的引用。 FragmentActivity首先会检查Activity是否能够处理事件,然后再检查它的任何片段,这与文档一致:
/**
 * 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;
    }
}

如下面片段所示,Activity在检查自身或其任何片段是否能够处理事件后,才将主页按钮作为最后的手段来处理。但是,对于FragmentManager的这个引用并不包含任何片段,片段位于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有一个父活动,我需要设置此标志以便无法拦截操作栏主页按钮。 总结一下问题:在具有DISPLAY_HOME_AS_UP设置的活动中,无法从片段中拦截单击操作栏主页按钮。
那么这是支持库中的错误吗?如果我针对较新的Android版本并放弃支持库,则似乎不会出现这种情况。
关于解决此问题的方法,我想我可以:
- 在ComposeActivityonOptionsItemSelected中,手动将事件传递给我的每个片段,看看它们是否能够处理它,然后再调用super。 - 在ComposeActivity中覆盖onMenuItemSelected并执行相同的操作。
有人遇到过这个问题吗?我应该在哪里记录错误?还有其他绕过此问题的方法吗?

1
你解决了这个问题吗?我正在尝试在片段的onOptionsItemSelected()中处理android.R.id.home,但它从未到达片段。 - Delblanco
这个链接可能会有帮助:https://dev59.com/GGUp5IYBdhLWcg3wzp5k?rq=1 - Delblanco
我目前正在一个基础活动中接收android.R.id.home操作,并在调用超级方法之前将其传递给所有片段。这是一个解决方法。 - darnmason
一样,我猜我希望有一个更好的解决方案。 - Delblanco
1
不是很理想。但是现在我们可以享受更新应用程序以支持Lollipop的乐趣了! - darnmason
1个回答

5

正如您所解释的那样,由于在Android中事件分发的流程,似乎子片段永远不会截取事件,因为它首先被Activity消耗掉了。

这是一种解决方法,但我正在做的是在Activity处理事件之前将事件传递给子片段。

在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: https://dev59.com/g3zaa4cB1Zd3GeqPUchz
    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);
}

有什么简单而巧妙的方法可以做到这一点...谢谢,对我的问题很有效。 - Manisha
我有多个ID,我应该使用哪个ID来处理这些片段? - squirrel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接