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閱讀 2,276評論 0 8
  • Fragment概述 Fragment是Activity中用戶界面的一個行為或者說是一部分。主要是支持大屏幕上動態...
    wangling90閱讀 11,573評論 5 75
  • 今年是我大學畢業后的第三年。 畢業后的這三年,我都干了些什么呢?畢業后的第一年,我找到了現在的工作,如今已經工作三...
    小維維_d991閱讀 404評論 3 3
  • 曾經的我們,有過五彩繽紛的夢,一切都信誓旦旦的樣子,直到時間一天天老去,夢碎了,我們才看清了它的顏色。那是一種平平...
    Anny藍月兒閱讀 350評論 0 1
  • 這些天,靜下來,偶爾會想起暑假前同事告訴我的一件事,就在離孩他爸老家三里地的一個小村莊里,一個十四五歲的男...
    半夏33閱讀 346評論 0 0