本文參考了activity、fragment源碼,源碼地址:Activity源碼
OptionsMenu 生成時 OnCreateOptionsMenuListener 調用順序
- 調用 activity 的 onCreateOptionsMenu
- 調用 activity 的 fragment 的 onCreateOptionsMenu 方法
- 調用 fragment 的 fragment 的 onCreateOptionsMenu 方法
即調用了 所有 可以調用的onCreateOptionsMenu方法來生成菜單。
需要在 fragment 的 onCreate 方法里 調用 setHasOptionsMenu 方法,才能讓 fragment 的 onCreateOptionsMenu 生效
ContextMenu 生成時 OnCreateContextMenuListener 調用順序
- 若通過 setOnCreateContextMenuListener 指定了 listener,則調用該 listener
- 若通過 activity 的 registerForContextMenu(View view) 方法 將 activity 指定為 view 的 OnCreateContextMenuListener,則調用 activity 的 onCreateContextMenu 方法。
- 若通過 fragment 的 registerForContextMenu(View view) 方法 將 fragment 指定為 view 的 OnCreateContextMenuListener,則調用 fragment 的 onCreateContextMenu 方法。
即只調用 指定 的OnCreateContextMenuListener來生成ContextMenu。
MenuItem 被點擊后 OnMenuItemClickListener 調用順序
- 若通過 setOnMenuItemClickListener 指定了 listener,則調用該 listener
為 MenuItem 設置 OnMenuItemClickListener 前,需要通過 Menu.add() 方法的返回值或者 Menu.getItem() 方法的返回值得到 MenuItem
- 判斷 MenuItem 來源,若來自 OptionsMenu,則調用 activity 的 onOptionsItemSelected 方法;若來自 ContextMenu,則調用 activity 的 onContextItemSelected 方法
- 若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,則通過下列流程:
- 調用 activity 的 onOptionsItemSelected 方法,true 則返回 true,false 則到 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。
- 如果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,則通過下列流程:
- 調用 activity 的 onContextItemSelected 方法,true 則 return true,false 則到 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 方法,則父類通過返回值告訴子類調用結果。