Android开发者指南-Action Bar[原创译文]

用户界面:

Action Bar

英文原文:http://developer.android.com/guide/topics/ui/actionbar.html

版本:Android 4.0 r1

译者注:黄色底色为未决译文

快速查看
  • 包含应用程序图标和activity标题的标题栏
  • 提供对菜单项和tab页之类的浏览模式的访问
  • 需要API level 11及以上版本
在本文中

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上的“滑动菜单”按钮(如果设备不提供菜单键时)。

Android开发者指南-Action <wbr>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的支持。

添加Action Bar

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

<manifest ... >
    <uses-sdkandroid: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即可。详情请参阅 定义Action Bar的样式 章节。

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

添加Action项

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

Android开发者指南-Action <wbr>Bar[原创译文]

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

当activity第一次被启动时,系统会调用activity中的 onCreateOptionsMenu() 来构建action bar和滑动菜单。正如 菜单 开发者指南中所述,在这个回调方法中你应该填写一个定义了菜单项的XML格式 菜单资源 。比如:

@Override
public booleanonCreateOptionsMenu(Menu menu){
    MenuInflater inflater =getMenuInflater();
   inflater.inflate(R.menu.main_activity,menu);
    return true;
}

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

如果你的菜单项同时具有标题和文本——带有android:titleandroid: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中。 不过,当一个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项都应该至少符合以下条件中的一个

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

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

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

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

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

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

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

此外,以下action永远都不应该作为action项:设置、帮助、Feedback或类似功能。请永远把它们放在滑动菜单中。

注意: 请记住,不是所有设备都提供专门的搜索按键,因此,假如搜索是应用的重要功能,那它就应该作为action项显示(通常显示为第一项,尤其你是用一个 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中使用了导航tab,一旦action项在窄屏上拆分显示,导航tab页就可以填满整个主action bar,而不是显示为“stacked action bar”。 特别地是,如果你已经禁用了action bar图标和标题(用 setDisplayShowHomeEnabled(false) 和 setDisplayShowTitleEnabled(false) ),那么导航tab页就会折叠放入主action bar中,就如同图3中的第二个设备上所显示的那样。

Android开发者指南-Action <wbr>Bar[原创译文]

图 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项使用的)。

向上导航

Android开发者指南-Action <wbr>Bar[原创译文]

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

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

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

Android开发者指南-Action <wbr>Bar[原创译文]

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

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

Android开发者指南-Action <wbr>Bar[原创译文]

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

导航设计

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

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

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);
    ActionBar actionBar = getActionBar();
    actionBar.setDisplayHomeAsUpEnabled(true);
    ...
}

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

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

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

添加一个Action View

Android开发者指南-Action <wbr>Bar[原创译文]

图 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空间。 详情请参阅下一节处理可折叠的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,通过调用 MenuItem 的 expandActionView() 和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

 Android开发者指南-Action <wbr>Bar[原创译文]

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

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"?>
<menuxmlns: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页

Android开发者指南-Action <wbr>Bar[原创译文]

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

 Android开发者指南-Action <wbr>Bar[原创译文]

图 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页的基本步骤如下:

  1. 实现 ActionBar.TabListener 接口。此接口中的回调方法对tab页中的用户作出响应,使你能切换fragment。
  2. 针对你要添加的每一个tab页,都要实例化一个 ActionBar.Tab 并通过调用 setTabListener() 设置ActionBar.TabListener 。还要用 setText() 和/或 setIcon() 设置tab页的标题和/或图标。
  3. 调用 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<extends Fragment> implementsActionBar.TabListener {
    private Fragment mFragment;
    private final Activity mActivity;
    private final String mTag;
    private final Class<T> mClass;

    
    public TabListener(Activity activity, String tag, Class<T> clz) {
        mActivity = activity;
        mTag = tag;
        mClass = clz;
    }

    

    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);

    此方法用到了 SpinnerAdapter 和 ActionBar.OnNavigationListener.

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

SpinnerAdapter和OnNavigationListener的示例

SpinnerAdapter 是一个为spinner widget提供数据的adapter,比如action bar中的下拉列表。SpinnerAdapter 是一个你可以重写的接口,但Android已包含了一些很有用的实现供你继承使用,诸如ArrayAdapter 和 SimpleCursorAdapter 。例如,以下是利用 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>
</pre>

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步中),并传入 ArrayAdapter 和 OnNavigationListener 。

在此例中,用户选中一个列表项时,会把一个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作为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">
        <itemname="android:actionBarTabTextStyle">@style/CustomTabTextStyle</item>
        <item name="android:actionBarDivider">@drawable/ab_divider</item>
        <itemname="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:actionBarStyle 和android:actionBarSplitStyle 。它们每个都增加了很多定义action bar属性的样式,包括用android:background、 android:backgroundSplit、 android: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>

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值