Android Menu生成与MenuItem响应流程简析

本文参考了activity、fragment源码,源码地址:Activity源码

OptionsMenu 生成时 OnCreateOptionsMenuListener 调用顺序

  1. 调用 activity 的 onCreateOptionsMenu
  2. 调用 activity 的 fragment 的 onCreateOptionsMenu 方法
  3. 调用 fragment 的 fragment 的 onCreateOptionsMenu 方法

即调用了 所有 可以调用的onCreateOptionsMenu方法来生成菜单。

需要在 fragment 的 onCreate 方法里 调用 setHasOptionsMenu 方法,才能让 fragment 的 onCreateOptionsMenu 生效

ContextMenu 生成时 OnCreateContextMenuListener 调用顺序

  1. 若通过 setOnCreateContextMenuListener 指定了 listener,则调用该 listener
  2. 若通过 activity 的 registerForContextMenu(View view) 方法 将 activity 指定为 view 的 OnCreateContextMenuListener,则调用 activity 的 onCreateContextMenu 方法。
  3. 若通过 fragment 的 registerForContextMenu(View view) 方法 将 fragment 指定为 view 的 OnCreateContextMenuListener,则调用 fragment 的 onCreateContextMenu 方法。

即只调用 指定 的OnCreateContextMenuListener来生成ContextMenu。

MenuItem 被点击后 OnMenuItemClickListener 调用顺序

  1. 若通过 setOnMenuItemClickListener 指定了 listener,则调用该 listener

为 MenuItem 设置 OnMenuItemClickListener 前,需要通过 Menu.add() 方法的返回值或者 Menu.getItem() 方法的返回值得到 MenuItem

  1. 判断 MenuItem 来源,若来自 OptionsMenu,则调用 activity 的 onOptionsItemSelected 方法;若来自 ContextMenu,则调用 activity 的 onContextItemSelected 方法
  2. 若activity没有重写上述两个方法或者返回值为false,则调用fragment对应的方法

可以看到 2 和 3 由 activity 和 fragment 的函数来响应,下文具体说明响应流程。

MenuItem 被点击后 activity 具体响应流程


若为MenuItem指定了 OnMenuItemClickListener,则调用指定的 OnMenuItemClickListener,否则调用 activity 的 onMenuItemSelected 方法。


activity 重写了 Window 父类的 onMenuItemSelected 的方法,当 MenuItem 被点击后回调 activity 的 onMenuItemSelected,通过 featureId 来判断是 OptionsMenu ( featureId==Window.FEATURE_OPTIONS_PANEL ) 还是 ContextMenu ( featureId==Window.FEATURE_CONTEXT_MENU )。下面列出将会涉及到的一些方法:

//activity
public boolean onMenuItemSelected(int featureId, MenuItem item)
public boolean onOptionsItemSelected(MenuItem item)
public boolean onContextItemSelected(MenuItem item)
public boolean onNavigateUp() 


//fragmentManager
public boolean dispatchOptionsItemSelected(MenuItem item)
public boolean dispatchContextItemSelected(MenuItem item)

//fragment
boolean performOptionsItemSelected(MenuItem item)
boolean performContextItemSelected(MenuItem item)
boolean onOptionsItemSelected(MenuItem item)
boolean onContextItemSelected(MenuItem item)

OptionsMenuItem调用流程

如果是 OptionsMenu,则通过下列流程:

  1. 调用 activity 的 onOptionsItemSelected 方法,true 则返回 true,false 则到 2
  2. 调用 fragmentManager 的 dispatchOptionsItemSelected 方法,true 则 return true,false 则到 3。dispatchOptionsItemSelected 的内部实现如下:
    fragmentManager 对象,即 activity 的 mFragments 成员变量,遍历 fragments,调用第一个 fragment 的 performOptionsItemSelected 方法,若 fragment 返回 true则 fragmentManager return true,否则调用下一个 fragment 的 performOptionsItemSelected 方法。如果所有 fragment 都返回 false,则 fragmentManager return false
    fragment内部流程如下:
  • 如果自身被隐藏,则return false,否则到下一步
  • 如果 fragment 的 optionsMenu 不可用则到下一步,否则调用 fragment 的 onOptionsItemSelected 方法,true 则 return true,否则到下一步
  • 调用 mChildFragmentManager 成员变量(也是一个 FragmentManager,用来管理 fragment 的 fragment)的 dispatchOptionsItemSelected 方法,通过子 fragment 对菜单进行响应,true 则 return true,否则 return false。
  1. 如果menuItem的itemID==android.R.id.home并且能够返回上一层,则调用activity的onNavigateUp或者Activity.mParent.onNavigateUpFromChild方法并return结果,否则return false,整个过程结束。第三点的具体实现代码如下:
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;

ContextMenuItem 调用流程

如果是 ContextMenu,则通过下列流程:

  1. 调用 activity 的 onContextItemSelected 方法,true 则 return true,false 则到 2
  2. 调用 fragmentManager 的 dispatchContextItemSelected 方法,并返回调用结果。dispatchContextItemSelected 的内部实现如下:
    fragmentManager 对象,即 activity 的 mFragments 成员变量,遍历 fragments,调用第一个 fragment 的 performContextItemSelected 方法,若 fragment 返回 true则 fragmentManager return true,否则调用下一个 fragment 的 performContextItemSelected 方法。如果所有 fragment 都返回 false,则 fragmentManager return false
    fragment内部流程如下:
  • 如果自身被隐藏,则 return false,否则到下一步
  • 调用 fragment 的 onContextItemSelected 方法,true 则 return true,否则到下一步
  • 调用 mChildFragmentManager 成员变量(也是一个FragmentManager,用来管理 fragment 的 fragment)的 dispatchContextItemSelected 方法,通过子 fragment 对菜单进行响应,true 则 return true,否则 return false。

补充说明

可以看到,activity 的 onMenuItemSelected 是有返回值的,既然 activity 已经是响应 MenuItemSelected 事件的最上层,这个返回值返回给谁呢?返回给子类。若子类调用了父类的 onMenuItemSelected 方法,则父类通过返回值告诉子类调用结果。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 一个Fragment看起来就是一个和Activity一样的用户界面。你可以结合多个Fragments到一个acti...
    kaiviak阅读 6,716评论 0 8
  • Fragment概述 Fragment是Activity中用户界面的一个行为或者说是一部分。主要是支持大屏幕上动态...
    wangling90阅读 13,968评论 5 75
  • 今年是我大学毕业后的第三年。 毕业后的这三年,我都干了些什么呢?毕业后的第一年,我找到了现在的工作,如今已经工作三...
    小维维_d991阅读 3,068评论 3 3
  • 曾经的我们,有过五彩缤纷的梦,一切都信誓旦旦的样子,直到时间一天天老去,梦碎了,我们才看清了它的颜色。那是一种平平...
    Anny蓝月儿阅读 2,823评论 0 1
  • 这些天,静下来,偶尔会想起暑假前同事告诉我的一件事,就在离孩他爸老家三里地的一个小村庄里,一个十四五岁的男...
    半夏33阅读 2,844评论 0 0