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 方法,则父类通过返回值告诉子类调用结果。

你可能感兴趣的:(Android Menu生成与MenuItem响应流程简析)