Перемещение кнопки ActionBar Home в фрагменте

Я могу успешно перехватить кнопку ActionBar home из моего NavigationDrawerFragment, который добавлен в мой MainActivity, например:

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

Однако в моем ComposeActivity с ComposeFragment это не работает. onOptionsItemSelected не вызывается для фрагмента.

Я отлаживал код, и проблема, похоже, сводится к дизайну библиотеки поддержки Android. Похоже, что как FragmentActivity, так и Activity имеют свои собственные ссылки на 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;
    }
}

Как показано в приведенном ниже фрагменте, Activity обрабатывает домашнюю кнопку в качестве последнего средства, после проверки того, сможет ли она или любой из ее фрагментов обработать событие. Но эта ссылка на FragmentManager не содержит фрагментов, фрагменты находятся в менеджере FragmentActivity. Поэтому событие будет проглочено классом Activity, если установлено ActionBar.DISPLAY_HOME_AS_UP.

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 и бросил библиотеку поддержки.

Что касается обходных путей для этого, я думаю, я мог:

  • В моем ComposeActivity onOptionsItemSelected я могу вручную передать событие каждому из моих фрагментов, видя, могут ли они обработать его до вызова супер.
  • Переопределить onMenuItemВыбран в ComposeActivity и сделать то же самое.

Кто-нибудь сталкивался с этим раньше? Должен ли я где-нибудь регистрировать ошибку? Любые другие идеи путей решения этой проблемы?

Ответ 1

Как вы объяснили, из-за потока того, как событие отправлено на Android, кажется, что дочерний фрагмент никогда не перехватит событие, потому что он сначала потребляет активность.

Это обходной путь, но то, что я делаю, передает событие дочерним фрагментам, прежде чем обрабатывается в 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/info/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);
}