转载请注明出处:http://blog.csdn.net/qinjuning
Android3.0 以后 Menu相关做了较大改变。ActionBar作为新的Menu形式粉墨登场了。
3.0之后常见的Menu或ActionBar有这四种:
图1:普通的ContextMenu 图2: ActionBar
图3: ActionMode(Contxt-ActionBar) 图4 屏幕下方的ActionBar-代码里称之为splitActionBar
ContextMenu, 为某个View registerContextMenu后,显示效果如图一。
调用流程为: 长按 ->showContextMenu -> DecorView:: showContextMenuForChild() , 此时会创建一个ContextMenuBuilder ,对应show一个MenuDialogHelper类,该类主要已listPopup的展示所有menuItem —> ContextMenuBuilder::show() ,调用createContextMenu创建ContextMenu项 —>View::createContextMenu() -> Activity::onCreateContextMenu()创建MenuItem()。
至此,ContextMenu的创建流程走完了。
注意: 有些控件例如ListView等,会特殊处理长按事件的默认操作,即采用ActionMode模式,而不是ContextMenu菜单。例如:AbsListView中performLongPress()中会判断是否设置了CHOICE_MODE_MULTIPLE_MODAL标记,如果设置了,即调用startActionMode 转换为ActionMode模式。
ActionBar初始化过程,
回到PhoneWindow::installDecorView中。3.0之后的默认布局就是:screen_action_bar.xml , 对应布局如下:
screen_action_bar.xml
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:fitsSystemWindows="true"
- android:orientation="vertical" >
- <com.android.internal.widget.ActionBarContainer
- android:id="@+id/action_bar_container"
- style="?android:attr/actionBarStyle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" >
- <com.android.internal.widget.ActionBarView
- android:id="@+id/action_bar"
- style="?android:attr/actionBarStyle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
- <com.android.internal.widget.ActionBarContextView
- android:id="@+id/action_context_bar"
- style="?android:attr/actionModeStyle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:visibility="gone" />
- </com.android.internal.widget.ActionBarContainer>
- <FrameLayout
- android:id="@android:id/content"
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="1"
- android:foreground="?android:attr/windowContentOverlay"
- android:foregroundGravity="fill_horizontal|top" />
- <com.android.internal.widget.ActionBarContainer
- android:id="@+id/split_action_bar"
- style="?android:attr/actionBarSplitStyle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:visibility="gone" />
- </LinearLayout>
会初始化以下几个自定义View:
ActionBarContainer : FrameLayout,管理下面的ActionBarView 和 ActionBarContextView ,只会visible一个View。
ActionBarView (对应图2):: com.android.internal.widget.ActionBarView 用于显示各种Menu的ViewGroup
ActionBarContextView (对应图3) :默认Gone, 这个自定义ViewGroup用来显示ActionMode下的Menu
ActionBarContainer -- splitActionBar (对应图4): 默认Gone, 当Menu过多时,可以将菜单放到该视图上。
判断是否需要显示splitActionBar是在PhoneWindow::installDecor创建的。即
android:uiOptions="splitActionBarWhenNarrow",默认是true.如果设置了,Menu将显示在该View上。
显示ActionBar的过程如下:
installDecor@PhoneWindowjaa
- // Post the panel invalidate for later; avoid application onCreateOptionsMenu
- // being called in the middle of onCreate or similar.
- mDecor.post(new Runnable() {
- public void run() {
- // Invalidate if the panel menu hasn't been created before this.
- PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
- if (!isDestroyed() && (st == null || st.menu == null)) {
- invalidatePanelMenu(FEATURE_ACTION_BAR);
- }
- }
- });
此时会获取Activity对应的 PanelFeatureState 对象。
—> PhoneWindow::preparePanel(PanelFeatureState st, KeyEvent event)
—> PhoneWindow::initializePanelMenu(st) ,会创建一个MenuBuilder对象,以后所有Menu就归他管理了,为ActionBar setMenu。
—> cb.onCreatePanelMenu(st.featureId, st.menu), 默认就是Activity的方法了。
—> Activity::onCreatePanelMenu , 创建MenuItem
—> cb.onPreparePanel(),默认就是Activity的方法了, onPrepare。
支持ActionBar对应的MenuBuilder也全部初始化OK了。
ActionBarView 管理了以下几种布局:
HomeLayout -- 显示左侧的应用程序图标,回退箭头等;
TitleLayout -- 如果存在titile,subtile则显示
不同模式下的视图:
STANDARD : ActionMenuView
MODE_LIST: adapter mSpinner, 通过代码setDropdownAdapter设置适配器
MODE_TAB : tab ScrollingTabContainerView ,管理所有TabView, ScrollingTabContainerView.TabView, 其选中背景图由selected熟悉控制.
MODE_LIST 和 MODE_TAB对应的视图只能add 其中1个。
ExpandView -- 如果某个Menu设置了 android:actionViewClass,即能expand collapse。
如果展开时,其他视图置为GONE状态,只显示HomeLayout和该ExpandView。
ActionBarView 得自动实现measure和layout操作。
ActionBarContainer -- splitActionBar 单纯管理一个 ActionMenuView。
ActionBarContextView 即处于ActionMode模式下的视图管理
自动在最左侧添加一个√的CloseButton。
可以设置TitleLayout或者自定义的CustomView
管理一个 ActionMenuView。 initForMode方法时会初始化1个ActionMenuPresenter,提供该视图。
ActionMenuView 核心主角登场,掌声鼓励。
功能:管理MenuItem的视图容器。每个Menu作为一个cell,如果在屏幕上显示不全,则会自动创建一个更多的View。
measure 或layout时,计算较为复杂。每个cell有个最小宽度单位。
ActionMenuItemView: 对应于每个MenuItem的View。由ActionMenuView管理。
BaseMenuPresenter: 根据MenuBuiler为每个MenuItem创建ActionMenuItemView 或自定义的 actionClass----由 android:actionViewClass 或者android:actionProvider提供。
方法解析:
initForMenu : Menu设置
getMenuView: 获得创建的ActionMenuView 视图容器
updateMenuView : 更新ActionMenuView 的子View
getItemView : 为每个MenuItem创建View对象
addItemView : 将创建的View对象添加至ActionMenuView 视图容器中。
bindItemView: 绑定数据
ActionMenuPresenter 继承于BaseMenuPresenter ,其构造方法提供了两个视图,分别传递Action ViewGroup和 Action View对象,用于其父类初始话布局对象。并且它重写了getItemView方法, 如果MenuItem提供了actionView则用它,否则用系统定义好的。另外,如果某个MenuItem 为expand或者collapse状态,则MenuPresenter相关事件会回调,更改MenuItem VISBILE状态。
点击回调事件流程:
ActionMenuItemView::onClick()
—>ActionMenuView::invokeItem
—> MenuBuilder::performItemAction
—> MenuItemImpl:: invoke()
—> MenuBuilder:: dispatchMenuItemSelected ,此时的Callback为PhoneWindow对象。
如果为ActionMode模式,
startActionMode() ,转换为ActionMode模式。流程如下:
PhoneWindow:: startActionMode()
—> Activity :: onWindowStartingActionMode
—> ActionBarImpl:: startActionMode 转换为ActionMode模式,初始化1个ActionModeImpl对象(内部构造1个新的MenuBuilder,用来管理该模式下的Menu),同时ActionContextView Visbile, ActionBarView隐藏。
—> 继续调用 ActionBarImpl :: dispatchOnCreate() , 回调Activity的onCreateActionMode 生成MenuItem
ActionMode的回调如下: ActionModeImpl:: onMenuItemSelected 继续回调给 PhoneWindow相关方法
ActionBarView::expandItemActionView 打开某个MenuItem对应的actionView, 会设置mExpandedActionView 请求重绘视图
ActionBarView::collapseItemActionView 折叠某个MenuItem对应的actionView
ActionProvider
提供actionViewClass 以及对应的操作。对应的View由onCreateActionView()返回。
吐槽下:发现csdn的博客编辑器还没有有道云笔记让人舒坦,悲了个催。
备注:Android 2.X使用ActionBar得使用ActionBarSherlock 和 v7 appcompat library
使用注意: ①、两者皆不支持Float Button,即自动显示更多的按钮.
②、ActionMode等当然不会有咯..