我可以成功地从我的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);
}
然而,在我的
ComposeActivity
和ComposeFragment
中,这种方法不起作用。在片段上不会调用onOptionsItemSelected
。我已经调试了代码,问题似乎归结于Android支持库的设计。看起来
FragmentActivity
和Activity
都有自己对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版本并放弃支持库,则似乎不会出现这种情况。
关于解决此问题的方法,我想我可以:
- 在
ComposeActivity
的onOptionsItemSelected
中,手动将事件传递给我的每个片段,看看它们是否能够处理它,然后再调用super。
- 在ComposeActivity
中覆盖onMenuItemSelected并执行相同的操作。有人遇到过这个问题吗?我应该在哪里记录错误?还有其他绕过此问题的方法吗?
onOptionsItemSelected()
中处理android.R.id.home
,但它从未到达片段。 - Delblancoandroid.R.id.home
操作,并在调用超级方法之前将其传递给所有片段。这是一个解决方法。 - darnmason