Android的菜单系统主要指的是ActionBar的Menu菜单。首先来看下Android菜单的使用方法:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.test_menu_new,menu);
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
menu.removeItem(R.id.aaaa);
return super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
return super.onOptionsItemSelected(item);
}
1. 在Activity刚创建的时候会执行一次onCreateOptionsMenu方法和onPrepareOptionsMenu方法。
2. 每次点击“更多”按钮的时候,都会执行一次onPrepareOptionsMenu方法。
3. 当选中某个菜单Item的时候执行onOptionsItemSelected方法。
ActionBar菜单的创建加载过程。
ActionBar及ActionBar上的菜单都是在Activity启动的时候创建的。当一个新的Activity启动的时候,ActivityManagerService调用ActivityThread的performLaunchActivity方法,此方法中会调用Activity的attach方法,为Activity初始化一些变量。在Activity的attach方法中会为每一个Activity创建一个对应的PhoneWindow对象。然后在PhoneWindow中会创建ActionBar及ActionBar相关的菜单。
Activity在启动初始化过程中会调用onCreate方法,然后调用setContentView来设置Activity的显示内容。我们就从setContentView来简单分析下Activity的menu创建过程。
Activity.setContentView
public void setContentView(View view) {
getWindow().setContentView(view);
initWindowDecorActionBar();
}
Activity的setContentView方法,调用了getWindow的setContentView方法。这个getWindow获取的就是Attach方法中创建的PhoneWindow对象。然后调用initWindowDecorActionBar方法,来初始化ActionBar操作的相关方法。
PhoneWindow.setContentView
public void setContentView(View view) {
setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
public void setContentView(View view, ViewGroup.LayoutParams params) {
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
……
} else {
mContentParent.addView(view, params);
}
……
}
PhoneWindow的setContentView方法调用了两个参数的setContentView方法。这个方法中只保留的比较重要的和我们分析相关的代码。
首先调用installDecor方法来创建这个Activity对应的整个Window的界面布局。然后调用mContentParent.addView方法,将自己在setContentView方法中传过来的布局文件添加到mContentParent布局中。
PhoneWindow.installDecor
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
mDecor.makeOptionalFitsSystemWindows();
final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
R.id.decor_content_parent);
if (decorContentParent != null) {
PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
if (!isDestroyed() && (st == null || st.menu == null) && !mIsStartingWindow) {
invalidatePanelMenu(FEATURE_ACTION_BAR);
}
}
}
}
installDecor方法主要作用是生成和设置整个phoneWindow的窗口布局文件。首先调用generateDecor方法生成一个DecorView对象。然后调用generateLayout方法来添加生成窗口的布局文件,返回了一个mContentParent对象,contentParent对象就是setContentView的时候要把设置的布局文件 添加到这个View对象中,作为这个View的子View。然后从PhoneWindow的布局文件中找到id为decor_content_parent 的View对象,decorContentParent就是PhoneWindow布局文件的根布局文件。
这个方法两个关键点:
1. generateLayout方法生成窗口的布局文件
2. invalidatePanelMenu创建菜单
PhoneWindow.generateLayout
protected ViewGroup generateLayout(DecorView decor) {
……
int layoutResource;
int features = getLocalFeatures();
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
……
} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
layoutResource = a.getResourceId(
R.styleable.Window_windowActionBarFullscreenDecorLayout,
R.layout.screen_action_bar);
} else