【问题标题】:Intercepting ActionBar Home button in Fragment在 Fragment 中拦截 ActionBar Home 按钮
【发布时间】:2014-02-21 15:28:11
【问题描述】:

我可以从我的NavigationDrawerFragment 中成功拦截ActionBar 主页按钮,该按钮添加到我的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是否可以处理事件,然后再检查它的任何片段,这与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;
    }
}

如下面的 sn-p 所示,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 并执行相同的操作。

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

【问题讨论】:

  • 问题解决了吗?我正在尝试处理片段的onOptionsItemSelected() 中的android.R.id.home,但它永远不会到达片段。
  • 我目前在基本活动中接收android.R.id.home 操作,并在调用超级方法之前将其传递给所有片段。一种解决方法。
  • 这里一样,我想我希望有更好的解决方案
  • 不理想。但是,嘿,现在我们很高兴更新我们的应用程序以支持 Lollipop!

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


【解决方案1】:

正如您所解释的,由于事件在 Android 中的调度流程,子片段似乎永远不会拦截该事件,因为它首先被 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: 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);
}

【讨论】:

  • 多么简单又好用的 hacky 方法啊……谢谢对我的问题很有用。
  • 我有多个 Id,我将使用哪个 Id 作为片段
猜你喜欢
  • 1970-01-01
  • 2012-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多