转自: http://www.apkbus.com/forum.php?mod=viewthread&tid=69132
Action Bar是ANDROID应用用到的重要开发资源之一,Action Bar提供了一致的应用导航和视图切换方式,也提供了突出的和容易的以可预见的方式执行重要操作的方式。 Action Bar一般位于屏幕顶部,包括四个可操作区域:应用图标或LOGO区域,用于视图控制的Spinner下拉菜单或TAB控件区域,Action button(也称为Action Item)区域,提供溢出菜单的Action overflow区域。
另外 Action Bar 还提供与上下文选中项相关的 Contextual Action Bar、以及与菜单项绑定的Action View以及提供 共享服务的 Share Action Provider。从功能上整个系统类图分成相对对立的三个部分,一是视图控制部分、二是 Action Provider部分、 三是菜单呈现部分。
![](http://www.apkbus.com/data/attachment/forum/201209/19/170053vie8dredd8qur7i2.jpg)
图 1 ActionBar 视图控制部分
图1为ActionBar视图控制部分类图。 Action Bar 包括几个显示区域,每个区域分别由不同的视图类型来显示,每个视图类型对应图1类图中的不同的视图对象。
应用图标对象所在视图对应ActionBarView对象的HomeView类型的内部视图对象mHomeLayout,ActionBarView的OnClickListener类型的回调对象mUpClickListener提供对应用图标操作的动作监听,从而通过回调传给实际ACTIVITY的onMenuItemSelected回调函数,在onMenuItemSelected回调函数中进行相应动作处理。
用于视图控制的Spinner下拉菜单对应ActionBarView中的Spinner对象mSpinner(通过创建一个SpinnerAdapter对象为其提供下拉菜单项),ActionBarView的AdapterView.OnItemSelectedListener类型的mNavItemSelectedListener是其事件的监听对象,当选中SpinnerAdapter对象的下拉项时触发事件,使mNavItemSelectedListener对象的onItemSelected回调函数被调用,onItemSelected中调用ActionBar.OnNavigationListener类型 的应用对象的onNavigationItemSelected函数进行相应事件处理。ActionBar.OnNavigationListener对象连同SpinnerAdapter对象都是通过 ActionBarImpl对象的setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback)函数传给ActionBarView对象的。为了打开下拉菜单功能还需要调用ActionBarImpl对象setNavigationMode函数设置NavigationMode为ActionBar.NAVIGATION_MODE_LIST模式。
Action Button对象所在视图对应AbsActionBarView对象的ActionMenuView类型的内部对象mMenuView。每个Action Button实际对应一个菜单项,可以采用菜单项一样的方式进行处理,在具体ACTIVITY 的onCreateOptionsMenu() 回调中进行菜单资源的读取和安装,菜单资源通过XML资源文件进行的定义。由菜单资源XML文件中的android:showAsAction属性定义一个菜单项为Action Button。 Action Button的呈现由ActionMenuPresenter对象负责。
溢出菜单和Action Button的子菜单也是在菜单资源 XML文件中进行定义,当在菜单XML资源文件中定义Action Button的android:showAsAction属性定义为"ifRoom"时,当活动条没有足够空间时Action Button显示到溢出菜单所在空间。溢出菜单的呈现由派生自MenuPopupHelper对象的OverflowPopup负责,Action Button的子菜单由派生自MenuPopupHelper对象的ActionButtonSubmenu负责呈现。
Contextual Action Bar对应ActionBarContextView类,ActionBarContextView视图由ActionBarImpl对象的startActionMode函数通过调用其initForMode函数进行初始化(以ActionModeImpl对象作为参数 ) ,ActionBarContextView视图的菜单采用ActionModeImpl对象的菜单,ActionModeImpl对象的菜单的生成及菜单项的事件处理都通过ActionModeImpl对象的ActionMode.Callback类型的回调对象处理,ActionBarContextView菜单的呈现也是由ActionBarContextView对象创建的相应的 ActionMenuPresenter对象负责。
ActionMode.Callback类型的回调对象通过startActionMode函数的参数传给ActionModeImpl对象,ActionMode.Callback类型的回调对象是用户为特定上下文视图定义的。因此用户为了使特定视图有上下文Action Bar,需要如下两部工作:
1 实现ActionMode.Callback回调接口,在ActionMode.Callback回调函数中生成相应菜单及完成菜单项的事件处理代码。
2 调用startActionMode(ActionMode.Callback callback)来显示上下文Action Bar。
Action View对象对应 ActionBarView对象中的mExpandedActionView, 由ExpandedActionViewMenuPresenter对象负责呈现,每个Action View对象与菜单项进行绑定,在 菜单资源XML文件中进行描述,由XML文件中的android:actionLayout及android:actionViewClass 属 性确定。
TAB 控件组对应对象ActionBarImpl中的TabImpl对象组,在ScrollingTabContainerView视图中显示,为了打开TAB导航模式,需要调用ActionBarImpl对象setNavigationMode函数设置NavigationMode为ActionBar.NAVIGATION_MODE_TABS。TabImpl对象、ActionBarImpl对象、ScrollingTabContainerView视图三者构成MVC模式关系,TabImpl对象对应Model, ActionBarImpl对象对于控制器,ScrollingTabContainerView对应视图 。
类图中的 ActionBarImpl提供对各个视图的控制功能。
Action Provider元素机制与Action View差不多,和Action View一样需要与菜单项绑定,也是在菜单资源的XML文件中为菜单项指定绑定的Action Provider对象 ,由 XML文件中的 android:actionProviderClass属性确定, 只是Action Provider更复杂,包括更多的对象,而Action View只对应一个视图对象。用户在使用Action Provider时,需要创建派生自ActionProvider的 具体 ActionProvider类,并酌情实现ActionProvider的相应回调接口,尤其在具体ActionProvider类的onCreateActionView回调函数中需要创建相应的视图,具体ActionProvider类可以具有独立的布局XML文件,在onCreateActionView回调中进行读取来生成相应视图。
![](http://www.apkbus.com/data/attachment/forum/201209/19/170053xvghbghupvgkrgej.jpg)
图2 ActionBar菜单呈现部分
图2 是 Action Bar相关视图对象的 菜单呈现及菜单构建相关类图。
Action Bar相关视图的 菜单呈现及菜单构建类图包括负责菜单构建过程的菜单构建对象(MenuBuilder)、负责菜单容器及菜单项的视图创建的菜单呈现对象(MenuPresenter)、菜单容器和菜单项视图对象(view)三类对象构成,三类对象构成MVP模式。
MenuBuilder对应MVP模式的model,MenuPresenter对应MVP模式的Presenter,视图对象对应View。MenuBuilder构建菜单的每一菜单项。 MenuPresenter 从MenuBuilder对象读取菜单项并生成相应的菜单项子视图,创建的菜单项子视图被添加到菜单容器视图中,MenuPresenter也提供对MenuBuilder对象及其菜单项的获取及其它操作。视图通过MenuPresenter获得和操作菜单和菜单项对应的视图,在MVP模式中视图和模式不直接交互。
ActionBar系统包括四个 MenuPresenter具体类,ActionMenuPresenter负责 Action Button及 Contextual Action Bar对应的菜单视图呈现,ExpandedActionViewMenuPresenter负责与菜单项绑定的Action View对象的视图呈现,OverflowPopup负责 溢出菜单对应的视图呈现,ActionButtonSubmenu负责子菜单对应的视图呈现。 OverflowPopup和 ActionButtonSubmenu都派生自MenuPopupHelper,由于 OverflowPopup呈现的菜单为MenuBuilder对象,而 ActionButtonSubmenu 呈现的菜单 对应SubMenuBuilder对象,因此MenuPopupHelper采用的 MenuBuilder对象是一个适配器对象, 采用了适配器模式对不同对象封装成相同的接口。
图2 类图中MenuItemImpl类是对应菜单项的具体实现类。 MenuBuilder对象创建的每一个 MenuItemImpl类型的 菜单项放在MenuBuilder对象的 的数组中。
MenuPopupHelper对象也登记为anchor视图的监听器,通过ViewTreeObserver对象来检测菜单所在anchor视图的变化。对于溢出菜单的anchor视图为OverflowMenuButton。
![](http://www.apkbus.com/data/attachment/forum/201209/19/170053331qzl9kq7vwklkl.jpg)
图3 action provider提供部分
图3 是系统提供的ShareActionProvider类及相关对象,提供快速存取提供 共享服务的 Action 。
整个类图主要包括 ShareActionProvider 、 ActivityChooserModel 、 ActivityChooserView 、 ActivityChooserViewAdapter 等对象。
派生自 ActionProvider 的具体类 ShareActionProvider ,用来实例化 ActivityChooserModel 、 ActivityChooserView 对象,并为 ActivityChooserView 对象设置数据模式,生成视图, 操作和获取Model信息,根据Model信息创建活动菜单。
ActivityChooserModel 、 ActivityChooserView 、 ActivityChooserViewAdapter 三者构成 MVC 模式,分别对应 Model 、 View 及采用 Adapter 模式的 Control , ActivityChooserView 通过 ActivityChooserViewAdapter 获取 ActivityChooserModel 中的活动信息, ActivityChooserModel 本身派生自 DataSetObservable ,可以在 ActivityChooserView 对象中为 ActivityChooserModel 对象登记一个 DataSetObserver 类型的对象, ActivityChooserModel 对象通过该对象向 ActivityChooserView 对象发送 ActivityChooserModel 对象中的数据变化通知。
ActivityChooserModel 对于通过 intent 从包管理器中获得的符合 intent 条件的活动记录(以 ActivityResolveInfo 类型保存在数组列表中 mActivites )的排序方法采用了策略模式,排序方法被封装成对象,在没有通过 setActivitySorter 方法设置排序方法时,采用默认排序方法,由 DefaultSorter 对象封装。 DefaultSorter 对象提供的排序方法是依据活动记录中的 weight 值进行排序,被排序的活动记录的 weight 值还依据记录在 HistoricalRecord ( HistoricalRecord 中的活动通过读私有的 XML 类型的共享历史文件获得)列表中的活动顺序进行修改,依据 HistoricalRecord 列表的从后往前的顺序为 mActivites 数组中对应的对象增加 weight 值,活动历史记录中越往后的记录在 mActivites 列表中对应活动增加的权值越小,最新的相应记录增加的权值越大。
HistoryPersister线程对象用于把HistoricalRecord列表中的记录保存到历史文件中。HistoryLoader线程对象用于读取历史文件到HistoricalRecord列表中。DataModelPackageMonitor对象用于监视数据包,在数据包更新时同步活动记录列表mActivites。ActivityChooserMode对象还通过Map类型的HashMap保证一个相同的历史文件只能实例化一个ActivityChooserModel对象,是单例模式的具体应用。
以下转自:http://blog.csdn.net/qinjuning/article/details/41984281
Android3.0 以后 Menu相关做了较大改变。ActionBar作为新的Menu形式粉墨登场了。
3.0之后常见的Menu或ActionBar有这四种:
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等当然不会有咯...