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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容

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