您的位置:首页 > 产品设计 > UI/UE

ActionBar官方Guide

2014-01-09 13:38 429 查看
action bar是窗口提供的特性之一,它标识了应用程序及用户当前所在的运行位置,并向用户提供当前可用的action和导航(navigation)模式。 在绝大多数需要向用户明显提供action或全程导航的activity中 ,你都应该使用action bar,因为action bar能够向用户提供一个跨应用的一致界面,系统会根据不同的屏幕参数完美地调整action
bar。 你可以用 ActionBar API控制action bar的行为和可见性,这些API自Android
3.0 (API level 11) 开始引入。 action bar的主要目标是:

为应用程序标题(brand)和用户所在位置提供一个专用的显示栏。

左边是应用程序图标或logo,右边是activity标题。不过,如果当前view由一个导航标签所标识,比如当前所选中的tab,那么你也可以选择去除activity标题。

为多个不同的应用程序提供一致的导航栏和改变view显示方式的可选项。

action bar为实现多 fragments 的切换提供tab导航支持。它还提供下拉列表,作为导航模式的替代方案,或者用于改善当前view的显示(比如按照各种规则对列表进行排序)。

用一种统一的方式,使得activity的关键action(诸如“搜索”、“新建”、“分享”等等)

显示于显著位置,并让用户易于访问。

通过把 选项菜单 中的菜单项直接作为“action项”放入action
bar,你可以向用户提供对action的快捷访问, “action项”还可提供一个“action view”,其中内嵌一个widget,用于放置更多可直接执行的action。 未用action项给出的菜单项放入滑动菜单(over menu)中,用设备上的菜单键(如果有的话)或者action
bar上的“滑动菜单”按钮(如果设备不提供菜单键时)。





图 1. Honeycomb
Gallery 应用中的action bar(在横向显示的手机上),左边显示的是logo,然后是导航标签(tab),右边是一个action项(和滑动菜单按钮)。

注意: 如果你要显示上下文关联action项,有关上下文关联action bar的信息,请参阅Menu文档。

Action Bar设计 关于设计规范请参阅Android Design的Action
Bar指南。

保持向后兼容性
如果想在应用程序中提供一个action bar并且还想与Android 3.0以前的版本保持兼容,那你需要在activity的layout中创建action bar(因为旧版本中不提供 ActionBar 类)。
为了帮助你实现兼容性, Action
Bar兼容性 例程给出了一个API层和action bar layout,使得你的应用程序可以使用其中的一些 ActionBar API,并且通过把传统的标题栏替换为自定义的action
bar layout,提供对旧版本的Android的支持。

目录

1 添加Action Bar

1.1 移除action bar

2 添加Action项

2.1 挑选你的action项

2.2 使用split action bar

3 使用应用程序图标进行导航

3.1 向上导航

4 添加一个Action View

4.1 处理可折叠的action view

5 添加一个Action Provider

5.1 使用ShareActionProvider

5.2 创建一个自定义action provider

6 添加导航Tab页

7 添加下拉式导航

7.1 SpinnerAdapter和OnNavigationListener的示例

8 定义Action Bar的外观样式

8.1 通用外观

8.2 Action项

8.3 导航tab页

8.4 下拉列表

8.5 高级样式

9 译者注


添加Action Bar

自Android 3.0 (API level 11)开始,所有使用 Theme.Holo 主题(或其衍生类)的activity都内置了action
bar,此主题是 targetSdkVersionminSdkVersion 属性设为大于等于"11"时的默认主题。例如:

<manifest ... >

<uses-sdk android:minSdkVersion="4"

android:targetSdkVersion="11" />

...

</manifest>

在上例中,应用程序需要的最低版本是API Level 4 (Android 1.6),但它的目标版本是API level 11 (Android 3.0)。 这种情况下,应用程序运行于Android 3.0及以上版本时,系统会为所有activity应用holographic主题,每个activity都会内置action bar。

如果要使用 ActionBar API,比如添加导航模式及修改action
bar外观样式,你应该把 minSdkVersion 设为大于等于"11"的值。如果你的应用程序需要支持旧版本的Android,有几种方法可以让你实现:在API
level 11及以上版本的设备上时使用 ActionBar API的有限子集,而在旧版本设备上仍然可以运行。更多保持向后兼容性的信息,请参阅侧栏。


移除action bar

如果不想让某个activity显示action bar,则可以把该activity的主题设置为 Theme.Holo.NoActionBar。比如:

<activity android:theme="@android:style/Theme.Holo.NoActionBar">

在运行时你还可以通过调用 hide() 来隐藏action
bar。比如:

ActionBar actionBar = getActionBar();

actionBar.hide();

当action bar隐藏后,系统会调整activity layout,以填满屏幕的全部可用空间。你可以用 show() 重新开启action
bar。

请小心,隐藏并移除action bar会导致你的activity布局进行重新调整,因为要计算action bar所占用的屏幕空间。 如果你的activity经常要隐藏和显示action bar(类似Android的图库Gallery应用),则你也许需要使用覆盖(overlay)模式。 覆盖模式会在你的activity layout表层绘制action bar,而不是在屏幕上。这样,隐藏及重新显示action bar时,你的layout就可以保持固定的大小。
要启用覆盖模式,为你的activity创建一个主题并设置 android:windowActionBarOverlay 为true即可。详情请参阅
[#Style">定义Action Bar的样式] 章节。

提示:如果你自定义了一个移除action bar的activity主题,请设置 android:windowActionBar 样式属性为false。不过,如果你使用主题来移除了action
bar,那么窗口就会完全禁用action bar,因此你就再也无法添加它了——调用 getActionBar() 将返回null。


添加Action项

有时你可能想要让用户能够快速访问 选项菜单 中的菜单项。要实现这一目标,你只要把这些菜单项声明为显示在action
bar中的“action项”即可。一个action项可以包含一个图标和/或一个文字标题。 如果菜单项无法作为action项显示,那系统会把它置于滑动菜单中。滑动菜单可以用设备上的菜单键(设备提供时)或action bar中的另一个按钮(设备不提供菜单键时)来调出的。





图 2. 两个带有图标、文字标题的action项,以及滑动菜单按钮。

当activity第一次被启动时,系统会调用activity中的 onCreateOptionsMenu() 来构建action
bar和滑动菜单。正如

菜单 开发者指南中所述,在这个回调方法中你应该填写一个定义了菜单项的XML格式 菜单资源 。比如:

@Override

public boolean onCreateOptionsMenu(Menu menu) {

MenuInflater inflater = getMenuInflater();

inflater.inflate(R.menu.main_activity, menu);

return true;

}

在此XML文件中,通过将 <item> 元素声明为带 android:showAsAction="ifRoom"属性 ,你可以让一个菜单项显示为action项。通过这种方式,菜单项将会仅当有空间时才显示在action
bar中,便于快速访问。如果没有空间的话,菜单项将会显示在滑动菜单中。

如果你的菜单项同时具有标题和文本——带有android:title和android:icon属性——则action项默认只显示图标。 如果需要显示文本标题,只要在android:showAsAction属性中加入"withText"即可。例如:

<?xml version="1.0" encoding="utf-8"?>

<menu xmlns:android="http://schemas.android.com/apk/res/android">

<item android:id="@+id/menu_save"

android:icon="@drawable/ic_menu_save"

android:title="@string/menu_save"

android:showAsAction="ifRoom|withText" />

</menu>

注意:
"withText"只是建议action bar显示文本标题。action bar会尽可能地显示标题,但如果显示完图标后空间比较拥挤,那也许就不会显示了。

如果用户选中一个action项,你的activity将会收到一个对 onOptionsItemSelected() 的调用,并传入android:id属性给出的ID——选项菜单中的所有项都会收到该回调方法。

确保为每个菜单项都定义android:title是非常重要的——即使你不需要它们显示为action项——这是因为以下三个原因:

如果action bar中没有足够的空间容纳action项,则菜单项会显示在滑动菜单中,并且只会显示文本标题。
弱视用户使用的屏幕朗读程序将会读取菜单项的文本标题。
假如action项只显示图标,用户还是可以长按它来调出提示信息(tool-tip),提示信息中就会显示action项的文本标题。

虽然android:icon总是可选项,但还是建议使用。要想了解图标设计的推荐规范,请参阅 Action
Bar图标设计规范。

注意:如果你通过
Fragment 类的 onCreateOptionsMenu 回调方法在fragment中添加菜单项,则当用户选中fragment中的某项时,系统将会调用fragment的 onOptionsItemSelected() 方法。但是,activity将优先处理该事件,因此系统会在fragment之前首先调用activity的同名 onOptionsItemSelected() 方法。

你还可以将action项声明为总是显示"always",而不是空间不足时置于滑动菜单中,在大多数场合,你不应该用"always"值强制把一个action项显示在action bar中。 不过,当一个[#ActionView">action view]不在滑动菜单中提供缺省action时,你也许需要用一个action项来给出此view,并且要保持可见。
请小心,过多的action项会让用户界面显得杂乱无章,并在窄屏设备上造成布局混乱。最好换用"ifRoom"来定义action项的显示方式,这样可以让系统在空间不足时把它移入滑动菜单。

关于创建定义action项的选项菜单,详情请参阅 菜单 开发者指南。


挑选你的action项

菜单项 vs 其它应用程序控件
作为一般规则,所有 选项菜单 中的菜单项(除action项外)应该对整个应用程序都有效,而不是只对某部分功能的界面才有效。
比如,假设你有一个多面板(pane)的layout,其中一个面板会播放视频,而另一个则列出所有的视频清单, 则视频播放器控件应该放在显示视频的那个面板中(而非action bar中),而action bar可以提供共享视频、把视频保存到收藏列表之类的action项。 而且,在确定一个菜单项是否应作为action项之前,也请先明确此菜单项是否对当前activity的全范围都有效。如果不是,那你就应该把它作为按钮放入activity layout中合适的上下文中。

你应该根据对某些关键因素的评价,对需要显示为action项的选项菜单项进行仔细挑选。一般而言,每个action项都应该至少符合以下条件中的一个:

经常使用:用户有十分之七以上的几率要用到此action,或者会连续多次使用它。

常用action示例:短信应用中的“新建信息”、在Android Market中“搜索”。

重要:你要让用户易于发现此action,或者不常使用但用户需要时就要能很方便地执行。

重要action示例:Wi-Fi设置中的“添加网络”、图库应用中的“切换到摄像头”。

典型:很多类似的应用通常都会在action bar中提供此action,因此用户也期望能在你的应用中看到此action。

典型action示例:email或社交应用中的“刷新”、People应用中的“新建联系人”。

如果确信有理由用作action项的菜单项超过了四个,那你就应该对他们的重要性进行仔细地评级,并努力让用作action项的数量不超过四个(利用"ifRoom"值即可,使得系统能在屏幕空间不足时把它们移入滑动菜单)。 即使宽屏上的空间够用了,你也不应该创建一排action项把用户界面都挤得满满的、看上去像桌面工具栏一样,因此请保持action项的数目为最小值。 此外,以下action永远都不应该作为action项:设置、帮助、Feedback或类似功能。请永远把它们放在滑动菜单中。

注意: 请记住,不是所有设备都提供专门的搜索按键,因此,假如搜索是应用的重要功能,那它就应该作为action项显示(通常显示为第一项,尤其你是用一个
[#ActionView">action view]来提供搜索功能时)。


使用split action bar

当你的应用运行于Android 4.0 (API level 14)及以上版本时,action bar增加了一个名为“split action bar”的模式。 启用split action bar后,如果在窄屏上(比如纵向显示的手持设备)运行activity,屏幕底部将会显示一个单独的bar,用于显示所有的action项。 把action bar拆分开、让多个action项分开显示,可以确保在窄屏上以合理的间隔显示所有的action项,并为顶部的导航条和标题留下足够的空间。

要启用split action bar很简单,只要把uiOptions="splitActionBarWhenNarrow"加入你的 <activity><application> manifest元素即可。

请注意,Android可以根据屏幕的尺寸采用多种方式来调整action bar的显示。action bar可以为不同的屏幕大小提供更佳的用户体验,使用split action bar只是其中一种选择。 采用这种方式,你还可以让导航tab页折叠放入主action bar中显示。 也就是说,如果你在action bar中使用了[#Tabs">导航tab],一旦action项在窄屏上拆分显示,导航tab页就可以填满整个主action bar,而不是显示为“stacked action bar”。 特别地是,如果你已经禁用了action
bar图标和标题(用 setDisplayShowHomeEnabled(false)setDisplayShowTitleEnabled(false) ),那么导航tab页就会折叠放入主action
bar中,就如同图3中的第二个设备上所显示的那样。





图 3. split action bar示意图,左边是带有导航tab页的,右边是禁用应用程序图标和标题的。

注意: 尽管 android:uiOptions 属性是自Android
4.0 (API level 14)才开始引入的,但你仍然可以在你的 minSdkVersion 小于"14"时安全地使用它,以保证与旧版本Android系统的兼容性。当运行于旧版本时,因为无法识别,系统会简单地忽略此XML属性。
在manifest中包含它的理由,只是因为必须面向API level 14及以上版本的平台进行编译。只要确保你的程序代码中没有公开使用其它 minSdkVersion 版本不支持的API——仅有XML属性的话是会被旧版本平台安全地忽略的。


使用应用程序图标进行导航

使用logo代替图标
默认情况下,系统在action bar中会使用应用程序图标,这通过 <application><activity> 元素中的 android:icon 属性进行定义。然而,假如你同时定义了 android:logo 属性,则action
bar会使用logo图片代替图标进行显示。
logo通常应该比图标宽一些,但应该不会包含不必要的文本。通常只有想以用户熟悉的传统方式来表示品牌时,才会使用logo。 较好的例子是YouTube应用的logo——这个logo代表了用户品牌,而应用程序的图标只是一个确认所需显示范围的修改版本。

默认情况下,应用程序图标显示在action bar的左侧。如果你愿意,可以让图标成为一个action项。作为用户对图标action的响应,你的应用程序必须执行以下两件事之一:

返回应用程序的初始"home" activity,或者
应用程序向上回退至上一层

当用户触摸图标时,系统会调用activity的 onOptionsItemSelected() 方法,并带入android.R.id.homeID。作为响应,你应该启动home
activity或让用户向上回退到应用程序的上一层。

如果你是把返回home activity作为应用程序图标的响应,则你应该在 Intent 中包含 FLAG_ACTIVITY_CLEAR_TOP 标志。如果你要启动的activity已经存在于当前任务中了,则用这个标志,可以将所有在其之上的activity都销毁,并把该activity推到前台。
因为回到“home”是等同于“返回”的action,你通常不应该创建一个新的home activity实例,因此加入这个标志常常是非常重要的。 否则的话,你的当前任务栈中可能会有一长串的的activity,其中包含了home activity的多个实例。 比如,以下是返回"home" activity的onOptionsItemSelected() 示例:

@Override

public boolean onOptionsItemSelected(MenuItem item) {

switch (item.getItemId()) {

case android.R.id.home:

// action bar中的应用程序图标被点击了,返回home

Intent intent = new Intent(this, HomeActivity.class);

intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

startActivity(intent);

return true;

default:

return super.onOptionsItemSelected(item);

}

}

用户可以从其它应用程序中进入当前activity,这时你也许还想加入 FLAG_ACTIVITY_NEW_TASK 标志。此标志表示,不管用户导航至“home”还是返回上一层,新的activity都加入当前任务栈,而是放入一个属于你的应用程序的栈。
比如说,如果用户通过其它应用提交一个intent ,启动了一个属于你的应用的activity,然后选中了action bar图标来回到home或向上回退一级,FLAG_ACTIVITY_CLEAR_TOP 标志将会启动属于你的应用程序任务中的这个activity(而不是当前任务)。
系统或是启动一个新的任务并把这个你的这个新activity作为根activity,或者,假如后台任务中存在该activity的实例的话,则把任务推到前台,该activity会收到 onNewIntent() 。因此,如果你的activity要从其它应用接收intent(声明了任何通用的intent过滤器),你通常应该在intent中加入 FLAG_ACTIVITY_NEW_TASK

intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);

关于这些标志及back栈的更多信息,请参阅 任务和Back栈 开发者指南。

注意: 如果你用图标来返回home activity,请注意自Android 4.0 (API level 14)开始,你必须显式地调用
setHomeButtonEnabled(true) 将图标用作action项(在之前的版本中,图标默认就是作为action项使用的)。


向上导航





图 4. Email应用的标准图标(左边)和“向上返回”图标(右边)。系统自动加上了“up”标记。

作为对传统的BACK导航——让用户返回到任务历史中的前一屏幕——的补充,你可以让action bar图标提供“向上”导航功能,这可以让用户在你的应用程序逻辑架构中层层返回。 举例来说,如果当前屏幕是位于应用程序的很深的层次中,触摸应用程序图标将会向上返回一层,直接返回到当前屏幕的父层架构中。

比如,图5展示了BACK按钮的动作,这时用户是从一个应用跳转到另一个应用程序的activity(特别是从People应用中选择一个联系人并撰写一封email时)。





图 5. 从People进入Email应用后,BACK按钮的行为

然而,如果用户写完email后还想呆在email应用中,则向上回退导航就允许用户在email应用中回退,而不是返回到前一个activity中。 图6展示了这种场景,用户还是进入了email应用,但是按下action bar图标来向上导航,而不是返回。





图 6. 从People应用进入Email应用后,向上导航(UP navigation)。

导航设计
关于向上(Up)和回退(Back)导航的区别,请参阅Android Design的导航指南。

要让图标执行向上导航(在图标边上显示“up”标记),调用 ActionBarsetDisplayHomeAsUpEnabled(true) 即可:

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

ActionBar actionBar = getActionBar();

actionBar.setDisplayHomeAsUpEnabled(true);

...

}

当用户触摸了图标,系统会调用activity的 onOptionsItemSelected() 方法,并带入android.R.id.home ID,正如上节[#Home">使用应用程序图标进行导航]所示。

请记得在 Intent 上使用 FLAG_ACTIVITY_CLEAR_TOP 标志,这样就不会创建已存在的父activity的新实例。比如,你未使用 FLAG_ACTIVITY_CLEAR_TOP 标志,则在向上回退后,BACK按钮实际上相对于应用程序的逻辑架构会把用户“往前”带,这会显得比较古怪。

注意: 如果让用户到达当前你的activity的路径有很多种,则up图标应该沿着用户到达当前activity的实际路径一路返回。


添加一个Action View





图 7. 上边是带有折叠action view的action bar,用于实现搜索功能;下边是用 SearchViewwidget展开的action
view。

action view是一个显示于action bar中的widget,用于替代某个action项按钮。例如,如果你的选项菜单里有一个“搜索”项,你可以用一个 SearchView widget来加入一个action
view,以替换掉按钮。如图7所示。 要在 菜单资源 中声明一个action
view项,使用android:actionLayout 或 android:actionViewClass 属性指定一个layout资源或所需的widget类即可。比如:

<?xml version="1.0" encoding="utf-8"?>

<menu xmlns:android="http://schemas.android.com/apk/res/android">

<item android:id="@+id/menu_search"

android:title="@string/menu_search"

android:icon="@drawable/ic_menu_search"

android:showAsAction="ifRoom|collapseActionView"

android:actionViewClass="android.widget.SearchView" />

</menu>

注意,android:showAsAction属性还包含了"collapseActionView"。 这是可选项,表示action view应该折叠进入按钮中,当用户选中了按钮,action view就会展开。
否则,action view默认就是可见的,即使用户不再使用也可能会占用有效的action bar空间。 详情请参阅下一节[#ActionViewCollapsing">处理可折叠的action view]。

如果需要给action view增加一些事件钩子函数,你可以在 onCreateOptionsMenu() 回调方法中进行。通过用菜单项的ID调用 findItem() ,可以获取action
view中的元素,然后调用getActionView()
例如,上例中的搜索widget可以如下获取:

@Override

public boolean onCreateOptionsMenu(Menu menu) {

getMenuInflater().inflate(R.menu.options, menu);

SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();

// 配置搜索信息并添加事件监听器

...

return super.onCreateOptionsMenu(menu);

}

使用搜索widget的详情,请参阅创建搜索界面


处理可折叠的action view

让Android 3.0支持action view
"collapseActionView"选项自Android 4.0 (API level 14)开始引入。 不过,如果你的应用支持旧版本系统的话,你还是应该声明"collapseActionView",以便更好地支持小屏幕设备。
运行Android 4.0及以上版本的设备将会把action view折叠显示,而旧版本系统将会显示为设计时的大小。
要加入该值,需要在编译时把目标版本设为Android 4.0及以上值。因为无法识别,旧版本的Android会忽略"collapseActionView"值 ,只要保证你的源代码中没有用到 minSdkVersion 不支持的其它API即可。否则你就要增加代码,在运行时进行适当的版本检查。

利用action view,你可以提供对更多action的快速访问,而不必改变activity和fragment,也不必替换action bar。 不过,默认就让action view显示出来也许并不合适。为了节省action bar的空间(特别是在小屏幕上显示时),你可以把action view折叠进入一个action项按钮中。 当用户选中按钮时,action view会显示在action bar上。 当action view折叠时,如果你已经用"ifRoom"定义了android:showAsAction,系统也许会把该项放入滑动菜单中,但用户选中该项时action
view仍然会显示在action bar上。 通过在android:showAsAction属性中加入"collapseActionView",你可以让你的action view支持折叠,如上述XML中所示。

因为用户选中时系统会展开action view,所以你不必在 onOptionsItemSelected 回调方法中作出响应。系统仍然会在用户选中该项时调用 onOptionsItemSelected ,但只要你不返回true(表示你已经自行处理了该事件),系统总是会展开action
view。 用户选中action bar上的“up”图标或按下BACK键时,系统也会在展开action view。 必要时,你可以用代码来展开或折叠action view,通过调用 MenuItemexpandActionView()collapseActionView() 即可。

注意: 虽然action view的折叠显示是可选项,我们仍然建议你在包含 SearchView 时总是折叠显示action
view。同时请注意,某些设备提供了专门的“搜索”键,当用户按下“搜索”键时,你也应该展开搜索action view。 只要重写activity的 onKeyUp()回调方法即可,请侦听 KEYCODE_SEARCH 事件,然后调用 expandActionView()

如果需要根据action view是否可见来更新你的activity,你可以定义一个 OnActionExpandListener ,并用setOnActionExpandListener() 对其进行注册,以实现展开和折叠时接收回调。例如:

@Override

public boolean onCreateOptionsMenu(Menu menu) {

getMenuInflater().inflate(R.menu.options, menu);

MenuItem menuItem = menu.findItem(R.id.actionItem);

...

menuItem.setOnActionExpandListener(new OnActionExpandListener() {

@Override

public boolean onMenuItemActionCollapse(MenuItem item) {

// 折叠时执行一些操作

return true; // 返回true以折叠action view

}

@Override

public boolean onMenuItemActionExpand(MenuItem item) {

// 展开时执行一些操作

return true; // 返回true以展开action view

}

});

}


添加一个Action Provider





图 8. 图集Gallery应用的屏幕截图,带有展开的ShareActionProvider 子菜单,用于显示需共享的目标。

与[#ActionView">action view]类似,action provider(用 ActionProvider 类定义)以自定义的layout替换了action项,不过它还能完全控制action项的行为。
当你为action bar中的一个菜单项声明action provider时,不仅能用自定义layout控制显示方式,而且能处理菜单项进入滑动菜单后的默认事件。它还能为action bar或滑动菜单提供一个子菜单。

例如, ShareActionProvider 是一个 ActionProvider 的扩展类,它通过在action
bar显示一个当前可用的分享目标应用列表,来帮助完成“分享”action。 你可以声明一个 ShareActionProvider 的实例来处理一个action项,而不是用传统action项那种提交 ACTION_SEND intent的方式。这个action
provider提供了一个带下拉列表的action view,列表中列出了所有可用的应用,action view还会处理 ACTION_SEND intent,即使菜单项显示在滑动菜单中也没问题。因此,当你使用类似的action
provider时,你不必对菜单项的用户事件进行处理。

要为一个action项声明action provider,在你的 菜单资源 中为相应的<item>元素定义android:actionProviderClass 属性即可,这里要用完全限定的action
provider类名。例如:

<?xml version="1.0" encoding="utf-8"?>

<menu xmlns:android="http://schemas.android.com/apk/res/android">

<item android:id="@+id/menu_share"

android:title="@string/share"

android:showAsAction="ifRoom"

android:actionProviderClass="android.widget.ShareActionProvider" />

...

</menu>

在本例中, ShareActionProvider 是所用到的action
provider。在这里,action provider公开接管了菜单项的控制,同时处理它在action bar中的显示方式和行为,以及滑动菜单中的行为。 你还必须提供一个显示于滑动菜单内的文本标题。

当action provider显示在滑动菜单中时,虽然它可以执行menu项默认的action,但是你的activity(或fragment)也能覆盖这个默认行为,通过在 onOptionsItemSelected() 回调方法中处理点击事件即可。如果你不在此回调方法中处理这个事件,则action
provider会收到 onPerformDefaultAction() 回调方法来进行处理。不过,假如action
provider提供了子菜单,那么你的activity将不会收到 onOptionsItemSelected() 回调,因为显示子菜单取代了默认的菜单项选中时的显示行为。


使用ShareActionProvider

如果你需要利用设备上的其它应用为你在action bar提供一个“共享”action(比如,用短信应用或社交应用分享照片),那么用 ShareActionProvider 将是很有效的实现途径,而不是添加一个提交 ACTION_SEND intent的action项。使用 ShareActionProvider 作为action项时,它会提供一个带可用应用下拉列表的action
view,并处理 ACTION_SEND intent(如图8所示)。

ShareActionProvider 实现了创建子菜单、弹出显示分享目标应用、处理点击事件(包括显示在滑动菜单中的情况)等等所有的逻辑——你需要编写的代码只是为菜单项声明action
provider并指定分享所用的intent。

默认情况下,根据用户选中的频率, ShareActionProvider 为每个分享目标应用都保存了一个优先级。分享目标应用使用越频繁,它在下拉列表的位置就越靠上,用得最多的应用作为缺省分享目标直接显示在action
bar中。 优先级信息默认是存放在一个私有文件中,文件名由 DEFAULT_SHARE_HISTORY_FILE_NAME 指定。如果你只针对一种action使用 ShareActionProvider 或其子类,那你应该继续使用这个缺省的历史记录文件,你就什么也不用做。
不过,假如你要对具有多种不同意义的action使用 ShareActionProvider 或其子类,那每一个 ShareActionProvider 都应该指定自己的文件来管理各自的历史记录。要为 ShareActionProvider 指定历史记录文件,调用 setShareHistoryFileName() 并给出一个XML文件名(例如,"custom_share_history.xml")即可。

注意: 尽管 ShareActionProvider 会根据使用频率调整分享目标应用的优先级,但其行为还是可以扩展的, ShareActionProvider 的扩展类可以执行其它操作,并基于历史记录文件来调整优先级(如果适用的话)。

为了加入 ShareActionProvider 简单地用"android.widget.ShareActionProvider"定义android:actionProviderClass 属性
即可,如上述XML所示。剩下的事情只是要定义你要用于分享的 Intent 。你必须调用 getActionProvider() 来获取与 MenuItem 关联的 ShareActionProvider ,然后调用 setShareIntent()

如果分享所用intent的类型取决于所选中的菜单项或其它activity生命周期内可能发生变化的可变量,那么你应该把 ShareActionProvider 保存到一个成员字段中,并在必要时调用setShareIntent() 进行更新。例如:

private ShareActionProvider mShareActionProvider;

...

@Override

public boolean onCreateOptionsMenu(Menu menu) {

mShareActionProvider = (ShareActionProvider) menu.findItem(R.id.menu_share).getActionProvider();

// 如果你使用的ShareActionProvider超过一个,且每个都需要有不同的action,

// 则使用以下语句来为每个ShareActionProvider指定唯一的历史记录文件。

// mShareActionProvider.setShareHistoryFileName("custom_share_history.xml");

// 设置默认的share intent

mShareActionProvider.setShareIntent(getDefaultShareIntent());

return true;

}

// 当你需要在应用程序之外更新share intent,请调用mShareActionProvider.setShareIntent()

目前 ShareActionProvider 可以处理用户与菜单之间的所有交互,你不必在 onOptionsItemSelected() 回调方法中处理点击事件。

关于使用share action provider的例子,请参阅 ActionBarShareActionProviderActivity


创建一个自定义action provider

当你需要创建一个带动态行为并能在滑动菜单中执行缺省action的action view,较好的解决方案是扩展 ActionProvider 来定义这些行为。创建一个属于你自己的action
provider,为自己提供一个精心组织的、可重用的组件,而不是在你的fragment或activity代码中处理各种action项的转换和行为。 如上节所述,Android为分享action提供了 ActionProvider 的一个实现:ShareActionProvider

要创建你自己的action provider,只要简单地扩展 ActionProvider 类即可,并实现合适的回调方法。最重要的是你应该实现以下代码:

ActionProvider()这个构造方法会把应用程序的

Context 传给你,你应该把其它回调方法需要用到的那些字段保存进去。

onCreateActionView()在这里定义用作action项的action view。请在构造方法中使用

Context 来实例化一个 LayoutInflater 并从XML资源中解析出你的action
view layout,然后挂接事件侦听器lisener。例如:

public View onCreateActionView() {

// 让action view填充action bar

LayoutInflater layoutInflater = LayoutInflater.from(mContext);

View view = layoutInflater.inflate(R.layout.action_provider, null);

ImageButton button = (ImageButton) view.findViewById(R.id.button);

button.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

// 执行某些操作...

}

});

return view;

}

onPerformDefaultAction()当菜单项被从滑动菜单中选中后,系统会调用本方法,action provider应该为菜单项执行一个默认的action。

但是,如果你的action provider通过 onPrepareSubMenu() 提供了子菜单,则子菜单总是会显示出来,即使菜单项在滑动菜单内也是如此。当然,存在子菜单时 onPerformDefaultAction() 也不会被调用了。

注意: 一个实现了 onOptionsItemSelected() 的activity或fragment可以覆盖action
provider的默认行为,通过处理选中(item-selected)事件即可(返回true),这时,系统将不会调用 onPerformDefaultAction()

关于 ActionProvider 扩展类的实例,请参阅 ActionBarSettingsActionProviderActivity


添加导航Tab页





图 9. Honeycomb
Gallery 应用中的 action bar tab页屏幕截图。





图 10. 在窄屏上折叠显示的tab页屏幕截图。

当你需要在activity中提供导航tab页时,使用action bar的tab页是一个极佳的选择(而不是用 TabWidget),因为系统会根据不同的屏幕尺寸对action
bar tab进行调整——屏幕够宽时放入主action bar,屏幕太窄时放入拆分后的bar(如同“stacked action bar”),参见图9和图10。

要用tab页实现多个fragment间的切换,你必须在每次选中tab页时处理fragment事务(transaction)。 如果你还不熟悉如何利用 FragmentTransaction 改变fragment,请先阅读 fragments 开发者指南。

首先,你的layout必须包含一个 ViewGroup ,你要把所有与tab页关联的 Fragment 放入其中。请确保给ViewGroup 分配一个资源ID,这样你就可以在你的tab页切换代码中引用它。
如果tab页填充了整个activity layout(除了action bar),那你的activity就根本不需要layout了(甚至连 setContentView() 都不需要调用了)。取而代之的是,你可以把所有fragment都放入默认的根 ViewGroup 中,你可以用android.R.id.content ID(可以在下面的fragment事务例程中找到此ID)引用它。
确定了fragment在layout中显示的位置之后,增加tab页的基本步骤如下:

实现ActionBar.TabListenersetContentView()接口。此接口中的回调方法对tab页中的用户作出响应,使你能切换fragment。
针对你要添加的每一个tab页,都要实例化一个ActionBar.Tab并通过调用setTabListener()设置ActionBar.TabListener。还要用setText()和/或setIcon()设置tab页的标题和/或图标。
调用addTab()把所有tab页都加入到action
bar中去。

阅读 ActionBar.TabListener 接口时,请注意回调方法只能给出被选中的 ActionBar.Tab 和一个让你执行fragment事务的FragmentTransaction ——它无法给出切换前后fragment的任何信息。因此,你必须在每个 ActionBar.Tab 和相应的 Fragment 之间自行建立关联关系(为了执行合适的fragment事务)。可以有很多方式来定义这个关联关系,这取决于你的代码设计。在以下例子中, ActionBar.TabListener 的实现代码给出了一个构造方法,其中每个新的tab页都使用自己的listener实例。每个listener实例都定义了多个成员字段,用于以后执行合适的fragment事务。

例如,以下是 ActionBar.TabListener 可能的一种实现代码,每个tab页都使用了自己的listener实例:

public static class TabListener<T extends Fragment> implements ActionBar.TabListener {

private Fragment mFragment;

private final Activity mActivity;

private final String mTag;

private final Class<T> mClass;

/** 每次新tab页创建时使用的构造方法

* @param activity 宿主Activity,用于实例化fragment

* @param tag fragment的标识符

* @param clz fragment类,用于实例化fragment

*/

public TabListener(Activity activity, String tag, Class<T> clz) {

mActivity = activity;

mTag = tag;

mClass = clz;

}

/* 以下是每个ActionBar.TabListener的回调方法*/

public void onTabSelected(Tab tab, FragmentTransaction ft) {

// 检查fragment是否已初始化

if (mFragment == null) {

// 如果没有,实例化并添加到activity中

mFragment = Fragment.instantiate(mActivity, mClass.getName());

ft.add(android.R.id.content, mFragment, mTag);

} else {

// 如果存在,就只是关联一下以便显示

ft.attach(mFragment);

}

}

public void onTabUnselected(Tab tab, FragmentTransaction ft) {

if (mFragment != null) {

// 解除关联fragment,因为另一个fragment将会建立关联

ft.detach(mFragment);

}

}

public void onTabReselected(Tab tab, FragmentTransaction ft) {

// 用户选择了已选中的tab页,通常什么也不做。

}

}

警告: 在所有上述的回调方法中,你都不得调用fragment事务的commit()——系统会替你调用,如果你自行调用可能会抛出异常。你也不能把这些fragment事务加入back栈。

在上例中,当某tab页被选中时,侦听器listener只是简单地把相应的fragment与activity layout关联( attach() )起来——如果还未实例化,则先创建fragment并添加( add() )到layout中(作为android.R.id.contentview
group的子项)。当tab页未选中时,则解除关联( detach() )。

ActionBar.TabListener 的实现代码已完成了大部分的工作。剩下的工作就只有创建各个 ActionBar.Tab 并添加到 ActionBar 中去了。此外,你还必须调用setNavigationMode(NAVIGATION_MODE_TABS) 来显示tab页。如果实际上是tab页的标题指明了当前的view,也许你还需要调用 setDisplayShowTitleEnabled(false) 来禁止显示activity的标题。
例如,以下代码添加了两个tab页,并用到了上述的listener:

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

// 注意不用setContentView()了,因为我们用了根android.R.id.content作为所有fragment的容器

// 为了显示tab页而设置action bar

ActionBar actionBar = getActionBar();

actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

actionBar.setDisplayShowTitleEnabled(false);

Tab tab = actionBar.newTab()

.setText(R.string.artist)

.setTabListener(new TabListener<ArtistFragment>(

this, "artist", ArtistFragment.class));

actionBar.addTab(tab);

tab = actionBar.newTab()

.setText(R.string.album)

.setTabListener(new TabListener<AlbumFragment>(

this, "album", AlbumFragment.class));

actionBar.addTab(tab);

}

注意: 上例中实现的 ActionBar.TabListener 只是多种可用方法之一。在 API
Demos 应用中,你将看到更多的方法。

如果你的activity终止了,你应该用 保存实例的状态 保存当前选中的tab页,以便在用户返回时可以打开合适的tab页。在保存状态时,你可以利用 getSelectedNavigationIndex() 查到当前选中的tab页,它将返回选中tab页的位置索引。

警告: 根据需要保存每个fragment的状态是非常重要的,这样,当用户用tab页切换fragment以及返回前一个fragment时,它们的外观能够保持不变。
有关保存fragment状态的信息,请参阅fragments开发者指南。

注意: 在某些场合,为了确保在action bar中的最佳显示效果,Android系统会把你的action bar tab页显示为一个下拉列表。


添加下拉式导航

作为activity中的另一种导航(或过滤)模式,action bar提供了一个内建的下拉列表。比如,该下拉列表可以为activity中的内容给出各种不同的排序模式以供选择。

启用下拉式导航的基本步骤如下:

1. 创建一个提供可选项的SpinnerAdapter,用于下拉式导航和layout显示。

2. 实现ActionBar.OnNavigationListener,用于定义用户选中列表中某项时的动作。

3. 用setNavigationMode()启用action
bar的导航模式。例如:

ActionBar actionBar = getActionBar();

actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);

注意: 你应该在activity的onCreate()方法中执行此步。

4. 用setListNavigationCallbacks()设置下拉列表的回调方法。比如:

actionBar.setListNavigationCallbacks(mSpinnerAdapter, mNavigationCallback);

此方法用到了 SpinnerAdapterActionBar.OnNavigationListener.

这些就是最基本的设置。当然,其中 SpinnerAdapterActionBar.OnNavigationListener 是最重要的步骤。你可以用很多方式来实现你的这些下拉式导航功能,以及各种本文未涉及的SpinnerAdapter (更多信息你应该参阅 SpinnerAdapter 类的参考手册。以下是带你入门的 SpinnerAdapterActionBar.OnNavigationListener 的简单示例。


SpinnerAdapter和OnNavigationListener的示例

SpinnerAdapter 是一个为spinner
widget提供数据的adapter,比如action bar中的下拉列表。 SpinnerAdapter 是一个你可以重写的接口,但Android已包含了一些很有用的实现供你继承使用,诸如 ArrayAdapterSimpleCursorAdapter 。例如,以下是利用 ArrayAdapter 创建一个SpinnerAdapter 的简捷方法,用了一个字符串数组作为数据源:

SpinnerAdapter mSpinnerAdapter = ArrayAdapter.createFromResource(this, R.array.action_list,

android.R.layout.simple_spinner_dropdown_item);

createFromResource() 方法用到了三个参数:应用程序的 Context 、字符串数组的资源ID、显示列表项的layout。
一个在资源文件中定义的 字符串数组 类似如下:

<?xml version="1.0" encoding="utf-8"?>

<resources>

<string-array name="action_list">

<item>Mercury</item>

<item>Venus</item>

<item>Earth</item>

</string-array>

createFromResource() 执行完后会返回 ArrayAdapter ,你可以把它传入 setListNavigationCallbacks() (上面第4步)。当然,在此之前你需要先创建好 OnNavigationListener

在你的 ActionBar.OnNavigationListener 实现代码中,需要处理用户选中下拉列表项时fragment的切换或其它activity上的变动。而listener中只需要实现一个回调方法:onNavigationItemSelected()

onNavigationItemSelected() 方法会收到列表项在列表中的位置索引,以及一个由 SpinnerAdapter 给出的唯一ID。

以下是实例化一个匿名 OnNavigationListener 的示例,它把一个 Fragment 插入R.id.fragment_container指定的layout容器:

mOnNavigationListener =new OnNavigationListener() {

// 获得下拉列表的ArrayAdapter所提供的字符串列表

String[] strings = getResources().getStringArray(R.array.action_list);

@Override

public boolean onNavigationItemSelected(int position, long itemId) {

// 用我们自己的Fragment类创建新的fragment

ListContentFragment newFragment = new ListContentFragment();

FragmentTransaction ft = openFragmentTransaction();

// 用此fragment替换fragment容器内的fragment

// 并将当前选中的名称作为标记名赋给fragment

ft.replace(R.id.fragment_container, newFragment, strings[position]);

// Apply changes

ft.commit();

return true;

}

};

现在 OnNavigationListener 的实例定义完成了,你可以调用 setListNavigationCallbacks() 了(第4步中),并传入 ArrayAdapterOnNavigationListener

在此例中,用户选中一个列表项时,会把一个fragment添加到layout中(替换R.id.fragment_container view中的当前fragment)。 此fragment会被赋予一个唯一标识的tag值,也即下拉列表中标识此fragment的那个字符串。

以下给出了一个本例中定义每个fragment的ListContentFragment类:

public class ListContentFragment extends Fragment {

private String mText;

@Override

public void onAttach(Activity activity) {

// 这是第一个收到的回调方法;

// 这里我们在fragment事务期间把fragment的文本设置为tag

super.onAttach(activity);

mText = getTag();

}

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

// 定义fragment的layout

// 我们只是定义 TextView 并设置文本为fragment的tag

TextView text = new TextView(getActivity());

text.setText(mText);

return text;

}

}


定义Action Bar的外观样式

如果你的应用中已经实现了一个自定义的widget,也许你还想重新设计action bar以适应应用的整体设计。为此,你需要使用Android的样式和主题框架,用特定的样式属性来重新定义action
bar的外观样式。

注意: 为了能够根据当前状态改变按钮的背景图片(选中、按下、未选中),所用的绘图资源(drawable resource)必须是一个state
list drawable

警告: 所有背景drawable都必须是Nine-Patch
drawables以允许缩放。Nine-Patch图片的高度应该小于40px,宽度应小于30px(对于中分辨率mdpi设备而言)。


通用外观

android:windowActionBarOverlay声明action bar是否应该覆盖activity layout,而不是平铺显示(例如,图集应用就使用了覆盖模式)。缺省值是false。

通常,action bar需要占用屏幕空间,而activity layout会填满剩余的空间。 当action bar为覆盖overlay模式时,activity layout会填满所有可用的空间,系统会在其上绘制action bar。如果在action bar显示和隐藏时,你的显示内容都需要保持固定大小和位置,那么覆盖模式可能非常有用。 你还可以纯粹为了视觉效果而使用它,因为action bar的背景可以是半透明的,用户仍然可以看见一部分action
bar后面的activity layout。

注意:Holo主题组默认就是用半透明背景来绘制action
bar。不过,你可以用自己定义的样式修改它,不同设备上的DeviceDefault主题可以默认使用不透明的背景。

当启用覆盖模式时,你的activity layout并不知晓在其之上存在着action bar。因此,你必须十分小心,不要在被action bar覆盖的区域内放置任何重要的信息或UI组件。 如果可能的话,你可以参考系统的 actionBarSize 值来确定action
bar的高度,这由你的XML layout定义。例如:

<SomeView

...

android:layout_marginTop="?android:attr/actionBarSize" />

你可能还想在运行时用 getHeight() 获取action
bar的高度。如果是在activity生命周期比较靠前的方法中调用,它可能不包含 stacked action bar(由于存在导航tab页)。 要明白如何在运行时确定全部高度(包括stacked action bar),请参阅Honeycomb
Gallery应用例程中的TitlesFragment类。


Action项

android:actionButtonStyle定义用于action项按钮的样式资源。android:actionBarItemBackground定义用作所有action项背景的绘图资源(自API level 14开始引入)。android:itemBackground定义用作所有滑动菜单项背景的绘图资源。android:actionBarDivider定义用作action项分隔符的绘图资源(自API level 14开始引入)。android:actionMenuTextColor定义action项的文本颜色。android:actionMenuTextAppearance定义action项显示文本的样式资源。android:actionBarWidgetTheme定义widget的主题资源,此widget作为[#ActionView">action views]嵌入action bar。(自API level 14开始引入)。


导航tab页

android:actionBarTabStyle定义action bar中tab页的样式资源。android:actionBarTabBarStyle定义导航tab页下方窄条的样式资源。android:actionBarTabTextStyle定义导航tab文本的样式资源。


下拉列表

android:actionDropDownStyle定义下拉式导航的样式(比如背景和文本样式)。比如,以下是定义了若干自定义action bar样式文件:

<?xml version="1.0" encoding="utf-8"?>

<resources>

<!-- the theme applied to the application or activity -->

<style name="CustomActivityTheme" parent="@android:style/Theme.Holo">

<item name="android:actionBarTabTextStyle">@style/CustomTabTextStyle</item>

<item name="android:actionBarDivider">@drawable/ab_divider</item>

<item name="android:actionBarItemBackground">@drawable/ab_item_background</item>

</style>

<!-- style for the action bar tab text -->

<style name="CustomTabTextStyle" parent="@android:style/TextAppearance.Holo">

<item name="android:textColor">#2456c2</item>

</style>

</resources>

注意: 请确保你的主题已在<style>标记中声明了父主题,以便从父主题中继承所有你未显式定义的样式。 修改action bar时,使用父主题是非常重要的,这样你只要重写那些你需要修改的样式即可,而不需要再去列出那些要保留的样式了(比如文本外观及action项的缩入空白)。

在你的manifest文件中,可以把你自定义的主题应用到整个应用程序,也可以只应用于某个单独的activity,如下所示:

<application android:theme="@style/CustomActivityTheme" ... />

关于样式和主题资源的使用,详见样式和主题


高级样式

如果你需要对action bar使用更高级的样式,你可以在activity的主题中包含 android:actionBarStyleandroid:actionBarSplitStyle 。它们每个都增加了很多定义action
bar属性的样式,包括用 android:backgroundandroid:backgroundSplitandroid:backgroundStacked 定义各种背景。如果你重写了这些action
bar样式,请确保定义了类似Widget.Holo.ActionBar 的父样式。

例如,如果你想改变action bar的背景,可以采用以下样式:

<?xml version="1.0" encoding="utf-8"?>

<resources>

<!-- the theme applied to the application or activity -->

<style name="CustomActivityTheme" parent="@android:style/Theme.Holo">

<item name="android:actionBarStyle">@style/MyActionBar</item>

<!-- other activity and action bar styles here -->

</style>

<!-- style for the action bar backgrounds -->

<style name="MyActionBar" parent="@android:style/Widget.Holo.ActionBar">

<item name="android:background">@drawable/ab_background</item>

<item name="android:backgroundStacked">@drawable/ab_background</item>

<item name="android:backgroundSplit">@drawable/ab_split_background</item>

</style>

</resources>

</div>

来自:http://wikidroid.sinaapp.com/Guide/topics/ui/actionbar
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: