`

Android开发之操作栏(Action Bar)知识大全

阅读更多

 

原文自:http://android.eoe.cn/topic/ui

 

操作栏是一个窗口功能用于确定应用程序和用户的位置,并提供给用户操作和导航模式。如果需要突出当前用户的操作或导航,应该使用操作栏,因为操作栏为用户提供了一个一致的接口,这个接口跨应用程序和系统,并且不同尺寸的屏幕适配操作栏的外观。你可以通过ActionBar API来控制动作栏的行为和可视性,这个API被添加在Android 3.0(API级别为11)。
操作栏设计的初衷是:

* 提供一个专门的空间来确定应用程序的标识和用户的位置。
* 这是在应用程序图标或者是左侧的标志以及Activity的标题帮助下完成的。如果当前视图的导航标签被标识,例如当前选项卡选中,你可能会选择删除该活动名称。
* 提供一致的导航和视图细化到不同的应用程序中。
* 操作栏提供了内置选项卡导航来进行切换。它还提供了一个下拉列表中,可以来用来替代导航模式或用来完善当前视图(比如按照不同的标准来排序列表)。
* 突出活动的关键动作(如“搜索”、“创建”、“共享”,等等。),便于用户在一个可预测的方法。
* 对于关键用户操作,你可以通过将项目从选项菜单直接在操作栏定义为操作项来提高访问速度。操作项也可以提供一个“操作窗口”,它用一个嵌入式部件来提供更多的及时活动的操作。它提供了一个更直接的行动行为的嵌入式部件。没有改进成操作项的菜单项在溢出菜单中还是有效的,用户既可以使用设备上的菜单按钮(设备上有按钮的时候),也可以使用操作栏中的溢出菜单按钮(当设备上不包含菜单按钮时)来显示这些操作项目。
http://developer.android.com/images/ui/actionbar.png

图1、Action Bar来源于Honeycomb的app库,从左边开始,依次为logo,导航标签与操作项(在右边插入溢出菜单按钮)。

* 注意*
有关Action Bar设计准则,可以阅读Android文档Action Bar的设计指南。

添加操作栏

从Android3.0(API级别11)开始,Action Bar包括在所有Activity中使用的Theme.Holo主题(或是继承Activity的一个子类),这是当targetSdkVersion或[http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#minminSdkVersion11”或更高时程序默认的主题。]属性设置为“
例如:



...

在这个例子中,应用程序设置的最低版本的API等级为4(Android 1.6),但它目标API级别为11(Android 3.0)。通过这样设置,当应用程序运行在Android 3.0或更高版本上时,该系统适用于全息每个Activity的主题,因此,每一个Activity包括Action Bar。

如果你想使用Action Bar的API,比如添加导航模式和修改操作栏样式,你应该设置minSdkVersion为“11”或是更高的版本。如果你想你的应用程序支持旧版本的Android,有很多办法可以让低版本的ActionBar的API的在支持API级别为11或更高的设备上,同时仍运行旧版本。参看sidebox保持向后兼容的信息。

移除操作栏

如果你不想为一个特定的Activity设置Action Bar,设置Activity主题为Theme.Holo.NoActionBar
例如:

您还在运行时通过调用hide())隐藏Action Bar。例如:

ActionBar actionBar = getActionBar();
actionBar.hide();

当Action Bar隐藏,系统的Activity调整布局来填补所有可用屏幕空间。你可以通过调用show())显示Action Bar。
隐藏和删除操作栏可能会使Activity重新调整布局,重新使用Action Bar所占用的空间。如果你的活动经常隐藏和显示操作栏(如在Android应用程序库),你可能想用叠加模式。叠加模式布局在Activity的顶部,而不是在屏幕空间上的Action Bar。这样,你的布局可以在Action Bar隐藏和重新出现时保持不变。要启用覆盖模式,创建Activity主题并且将android:windowActionBarOverlay属性值设置为true。欲了解更多信息,请参阅样式的Action Bar章节。

提示:如果你有一个删除了操作栏的定制化的Activity主题,它把android:windowActionBar样式属性设置为false。但是,如果你使用了删除操作栏的一个主题,那么,创建窗口将不允许操作栏再显示,因此,你不能在以后给这个Activity添加操作栏---因为getActionBar()方法将返回null。

添加操作项

有时你可能想让用户从选项菜单中直接访问项目。要做到这一点,你可以声明该菜单项为Action Bar中的一个“action item”。一个“action item”包括一个图标和/或文字标题。如果一个菜单项不作为一个“action item”,系统会将菜单项放置在溢出菜单。溢出菜单显示设备菜单“按钮(如果设备提供)或在操作栏中的按钮(如果设备不提供”菜单“按钮)。

首次启动Activity时,系统通过在activity调用onCreateOptionsMenu()方法来填充action bar和溢出菜单(overflow menu)。在菜单开发指南中讨论的,它是在这个回调方法,你应该夸大一个XML定义菜单项的菜单资源。例如:

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

http://developer.android.com/images/ui/actionbar-item-withtext.png

图2两个action item的图标和文字标题,以及溢出菜单按钮。

在XML文件中,你可以通过声明android:showAsAction="ifRoom" 成为这个条目的元素,从而使使一个菜单项变为action item。通过这种方式,当有可用空间时,菜单项才会出现在action item的快速访问栏。如果没有足够的空间,该item将出现在溢出菜单。

如果你的菜单项同时提供标题和图标--同时具有Android:titile和android:icon属性,action item默认只会显示图标。但如果你想显示文字标题,必须添加“withText”到Android:showAsAction属性中。例如:

<?xml version"utf-8"?>


注:“withText”值应是以一个操作栏的提示文本的标题出现。但如果一个图标无法使用或者空间受限,action bar的标题可能无法显示。

当用户选择一个action item,activityon调用一个OptionsItemSelected()​​方法,通过Android:id属性获取的ID来接收在选项菜单中的所有item中相同的回调。
这一点很重要,你总是为每个菜单项定义android:title,即使你不在显示的action item声明标题,原因有三:

  • 如果在action bar中没有足够的空间提供给action item,该菜单项出现溢出“菜单中,而且只有标题显示。
  • 屏幕阅读器为视障用户读出菜单项的标题。
  • 如果action item只有图标,用户可以长按item显示的工具提示,显示action item的标题。

Android的图标始终是可选的。但对于图标设计建议,详情参考Action Bar图标设计指引。

注:如果您添加了菜单项,从一个片段,通过片段类的onCreateOptionsMenu,
回调然后系统调用各自onOptionsItemSelected()的方法,当用户选择该片段片段的项目之一。
然而,该活动得到一个机会来处理事件,所以系统调用活动onOptionsItemSelected()的片段,然后再调用相同的回调函数。

你也可以定义一个item“总是”为action item,以避免当空间有限时被放到溢出菜单中去。但在大多数情况下,不应该设置“always”这个值来强制使一个item出现在action bar中。然而,然而,当提供的是一个“活动视图(action view)”而不是提供“溢出菜单”中的默认动作时,你可能需要这个item总是出现。要注意的是过多的action item,会导致创建出来的UI杂乱不堪,并且在窄屏幕的设备上会出现布局问题。最好使用“ifRoom”,而不是要求一个item出现在action bar中,但在没有足够的空间时,应当允许系统将它移动到溢出菜单。
对于有关创建选项菜单定义action item的详细信息,请参阅“菜单”开发人员指南。

选择你的操作项

你应该应通过评估的几个关键特性,仔细从选项菜单中选出action item。在一般情况下,每个action item,至少是下列操作之一:
* 1、常用性:用户70%的时间需要访问或需要连续多次使用。常用性例子:在消息应用程序和“搜索”谷歌播放的“新信息”。
* 2、重要性:用户能够很容易地发现,或者如果不经常使用,在少数情况用户确实需要它的时候,可以毫不费力地执行,这一点是很重要的。重要性例子:“加入网络”Wi-Fi设置“切换到相机”在库应用程序。
* 3、典型性:这是一个通常在类似的应用程序的操作栏中提供的行动,因此,用户希望自己找到它。典型性例子:电子邮件或社会的应用,“刷新”和“新接触”在应用程序。

如果你认为四个以上菜单项可以合理的作为action item,那么你应该仔细考虑其相对水平的重要性,并尽量设置不超过四个的菜单项的action item(这样设置“ifRoom”这个值,在一些空间有限的小屏幕上,系统把一些菜单项放到溢出菜单背面)。即使是宽屏幕上,你也不要创建一个杂乱的UI的action item,冗长得看起来像一个桌面工具栏,应该要使action item的数量保持到最低限度。
此外,下列行为不应该出现行动项目中:设置,帮助,反馈,或查找相似,始终将这些放在溢出菜单中。

注意:请记住,并非所有的设备都提供了一个搜索专用硬件按钮,因此,如果是应用程序的重要功能,它应该始终作为一个action item(通常作为第一个item,特别是如果你提供的是一个action view)。

菜单项对比其他应用程序的控制
作为一般规则,在选项菜单中(更不用说action item)的所有项目应该有一个应用程序的全局影响力,而不是只影响一小部分的接口。例如,如果你有一个多窗格的布局和一个窗格显示一个视频,而另一个列出的所有视频,视频播放器的控件应出现在包含视频窗格(而不是在操作栏),而action bar可能会提供action item来共享视频或保存视频到收藏夹列表中。
所以,在​​决定是否应该设置一个菜单项的action item前,先确保该项目当前activity是否有一个全局范围。如果没有,那么你应该设置一个按钮放置在适当的范围内的布局中。

使用分裂的操作栏

当您的应用程序上运行Android 4.0系统(API 14级)或更高级别时,还有一个额外的模式可称action bar为“split action bar”。当在一个狭窄的屏幕运行启用split action bar时,会在屏幕的底部出现一个action bar显示所有action item。分裂action bar用来分开action item,确保分配合理数量的空间来在一个狭窄的屏幕上显示所有的action item,而空间留给顶端的导航和标题元素。
使用 split action bar,只需添加uiOptions=“splitActionBarWhenNarrow”,到你的或清单元素。

要知道Android在各种不同的方式,根据当前的屏幕大小调整操作栏的外观。采用分体式操作栏只是一个选项,您可以启用允许操作栏,以进一步为不同的屏幕尺寸,优化用户体验。这样做,你也可以让操作栏可以折叠成主要的操作栏导航标签。也就是说,如果你在你的动作条中使用的导航标签,一旦操作项狭窄的屏幕上分离,导航标签可以融入的主要操作栏,而不是被分隔成的“折叠的操作栏”。具体来说,如果你禁用操作栏中的图标和标题(setDisplayShowHomeEnabled(false)和setDisplayShowTitleEnabled(false)),然后将主要动作栏的导航标签收合,如图3中的第二个设备。

http://developer.android.com/images/practices/actionbar-phone-splitaction.png

图3。模拟栏左侧的导航标签的分裂行动;与应用程序图标和标题右侧的禁用。

使用应用程序图标来导航

默认情况下,应用程序图标显示在操作栏的左边。你能够把这个图标当做操作项来使用。应用程序应该在这个图标上响应以下两个操作之一:
* 返回应用程序的“主”Activity;

  • 向应用程序上级页面导航。

当用户触摸这个图标时,系统会调用Activity带有android.R.id.home ID的onOptionsItemSelected()方法。在这个响应中,你既可以启动主Activity,也可以返回你的应用程序结构化层次中用户上一步操作的界面。

如果你要通过应用程序图标的响应来返回主Activity,那么就应该在Itent对象中包括FLAG_ACTIVITY_CLEAR_TOP标识。用这个标记,如果你要启动的Activity在当前任务中已经存在,那么,堆栈中这个Activity之上的所有的Activity都有被销毁,并且把这个Activity显示给用户。添加这个标识往往是重要的,因为返回主Activity相当与一个回退的动作,因此通常不应该再创建一个新的主Activity的实例,否则,最终可能会在当前任务中产生一个很长的拥有多个主Activity的堆栈。

例如,下例的onOptionsItemSelected()方法实现了返回应用程序的主Activity的操作:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// app icon in action bar clicked; go 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标识。这个标识确保在用户返回主页或上级页面时,新的Activity不会被添加到当前的任务中,而是在属于你自己的应用程序的任务中启动。例如,如果用户通过被另一个应用程序调用的Intent对象启动了你的应用程序中的一个Activity,那么选择操作栏图标来返回主页或上级页面时,FLAG_ACTIVITY_CLEAR_TOP标识会在属于你的应用程序的任务中启动这个Activity(不是当前任务)。系统既可以用这个新的Activity做根Activity来启动一个新的任务,也可以把存在后台的拥有这个Activity实例的一个既存任务带到前台来,并且目标Activity会接受onNewIntent()回调。因此,如果你的Activity要接收另一个应用程序的Intent对象,那么通常应该给这个Intent对象添加FLAG_ACTIVITY_NEW_TASK标识,如:

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

注意:如果你要使用应用图标来返回主页,要注意从Android4.0(API 级别 14)开始,必须通过调用setHomeButtonEnabled(true)方法确保这个图标能够作为一个操作项(在以前的版本,默认情况下,这个图标就能够作为一个操作项)。

向应用程序上级页面导航

作为传统的回退导航(把用户带回任务历史中的前一个窗口)的补充,你能够让操作栏图标提供向上级页面导航的功能,它应用把用户带回到你的应用程序的上级页面。例如,当前页面时你的应用程序层次比较深的一个页面,触摸应用程序图标应该返回返回上一级页面(当前页面的父页面)。
http://developer.android.com/images/ui/actionbar-logo.png

图4. Email应用程序的标准图标(左)和向上导航图标(右)。系统会自动添加向上指示。

例如,图5演示了当用户从一个应用程序导航到一个属于不同应用程序的Activity时,“回退”按钮的行为。
http://developer.android.com/images/ui/actionbar-navigate-back.png

图5. 在从People(或Contacts)应用程序进入Email应用程序之后,回退按钮的行为。

但是,如果在编辑完邮件之后,想要停留在Email应用程序中,那么向上导航就允许你把用户导航到Email应用程序中编辑邮件页面的上级页面,而不是返回到前一个Activity。图6演示了这种场景,在这个场景中,用户进入到Email应用程序后,不是按回退按钮,而是按操作栏图标来向上导航。

http://developer.android.com/images/ui/actionbar-navigate-up.png

图6. 从People应用进入Email应用后,向上导航的行为。

要是应用程序图标能够向上导航,就要在你的ActionBar中调用SetDisplayHomeAsUpEnabledtrue(true)方法。

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

1
2
3
4
setContentView(R.layout.main);
ActionBar actionBar = getActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
...

}

当用户触摸这个图标时,系统会调用带有android.R.id.home ID的onOptionsItemSelected()方法。

请集中要在Intent对象中使用FLAG_ACTIVITY_CLEAR_TOP标识,以便你不会这个父Activity存在的情况下,再创建一个新的实例。例如,如果你不使用FLAG_ACTIVITY_CLEAR_TOP标识,那么向上导航后,再按回退按钮,实际上会把用户带到应用程序的下级页面,这是很奇怪的。

注意:如果有很多用户能够到达应用程序中当前Activity的路径,那么,向上图标应该沿着当前Activity的实际启动路径逐步的向会导航。

添加操作视窗

操作视窗是作为操作项目按钮的替代品显示在操作栏中的一个可视构件。例如,如果你有一个用于搜索的可选菜单项,你可以用SearchView类来替代操作栏上的搜索按钮,如图7所示:

http://developer.android.com/images/ui/actionbar-searchview.png

图7. 折叠(上)和展开(下)的搜索视窗的操作栏

要个菜单资源中的一个项目声明一个操作视窗,你既可以使用android:actionLayout属性也android:actionViewClass属性来分别指定一个布局资源或要使用的可视构件类。例如:

<?xml version"utf-8"?>


android:showAsAction属性也可包含“collapseActionView”属性值,这个值是可选的,并且声明了这个操作视窗应该被折叠到一个按钮中,当用户选择这个按钮时,这个操作视窗展开。否则,这个操作视窗在默认的情况下是可见的,并且即便在用于不适用的时候,也要占据操作栏的有效空间。

如果需要给操作视窗添加一些事件,那么就需要在onCreateOptionsMenu()回调执行期间做这件事。你能够通过调用带有菜单项ID的findItem()方法来获取菜单项,然后再调用getActionView()方操作视窗中的元素。例如,使用以下方法获取上例中的搜索视窗构件。

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.options, menu);
SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
// Configure the search info and add any event listeners
...
return super.onCreateOptionsMenu(menu);
}

处理可折叠的操作视窗

操作视窗让你在不改变Activity或Fragment的情况下,就可以给用户提供快捷的访问和丰富的操作。但是,默认情况下让操作视窗可见可能不太合适。要保证操作栏的空间(尤其是在小屏幕设备上运行时),你能够把操作视窗折叠进一个操作项按钮中。当用户选择这个按钮时,操作视窗就在操作栏中显示。被折叠的时候,如果你定义了android:showAsAction=”ifRoom”属性,那么系统可能会把这个项目放到溢出菜单中,但是当用户选项了这个菜单项,它依然会显示在操作栏中。通过给android:showAsAction属性添加“collapseActionView”属性值,你能够让操作视窗可以折叠起来。

因为在用户选择这个项目时,系统会展开这个操作视窗,所以你不必要在onOptionsItemSelected()回调方法中响应这个菜单项。在用户选择这个菜单项时,系统会依然调用onOptionsItemSelected()方法,但是除非你在方法中返回了true(指示你已经替代系统处理了这个事件),否则系统会始终展开这个操作视窗。

当用户选择了操作栏中的“向上”图标或按下了回退按钮时,系统也会把操作视窗折叠起来。

如果需要,你能够在代码中通过在expandActionView()和collapseActionView()方法来展开或折叠操作视窗。

注意:尽管把操作视窗折叠起来是可选的,但是,如果包含了SearchView对象,我们推荐你始终把这个视窗折叠起来,只有在需要的时候,由用户选择后才把它给展开。在提供了专用的“搜索”按钮的设备上也要小心了,如果用户按下了“搜索”按钮,那么也应该把这个搜索视窗给展开,简单的重写Activity的onKeyUp()回调方法,监听KEYCODE_SEARCH类型的按键事件,然后调用expandActionView()方法,就可以把操作视窗给展开。

如果你需要根据操作视窗的可见性来更新你的Activity,那么你可以定义一个OnActionExpandListener事件,并且用setOnActionExpandListener()方法来注册这个事件,然后就能够在操作视窗展开和折叠时接受这个回调方法了,如:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.options, menu);
MenuItem menuItem = menu.findItem(R.id.actionItem);
...

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
menuItem.setOnActionExpandListener(new OnActionExpandListener() {
    @Override
    public boolean onMenuItemActionCollapse(MenuItem item) {
        // Do something when collapsed
        return true;  // Return true to collapse action view
    }

    @Override
    public boolean onMenuItemActionExpand(MenuItem item) {
        // Do something when expanded
        return true;  // Return true to expand action view
    }
});

}

添加一个操作提供器

与操作视窗类似,操作提供器(由ActionProvider类定义的)用一个定制的布局代替一个操作项目,它还需要对所有这些项目行为的控制。当你在操作栏中给一个菜单项声明一个操作项目时,它不仅要一个定制的布局来控制这个菜单项的外观,而且当它在显示在溢出菜单中时,还要处理它的默认事件。无论是在操作栏中还是在溢出菜单中,它都能够提供一个子菜单。

例如,ActionProvider的扩展类ShareActionProvider,它通过在操作栏中显示一个有效的共享目标列表来方便共享操作。与使用传统的调用ACTION_SEND类型Intent对象的操作项不同,你能够声明一个ShareActionProvider对象来处理一个操作项。这种操作提供器会保留一个带有处理ACTION_SEND类型Intent对象的应用程序的下拉列表,即使这个菜单项显示在溢出菜单中。因此,当你使用像这样的操作提供器时,你不必处理有关这个菜单项的用户事件。

要给一个操作项声明一个操作提供器,就要在菜单资源中对应的元素中定义android:actionProviderClass属性,提供器要使用完整的类名。例如:

<?xml version"utf-8"?>


...

在这个例子中,用ShareActionProvider类作为操作提供器,在这里,操作提供器需要菜单项的控制,并处理它们在操作栏中的外观和行为以及在溢出菜单中的行为。你必须依然给这个菜单项提供一个用于溢出菜单的文本标题。

尽管操作提供器提供了它在溢出菜单中显示时所能执行的默认操作,但是Activity(或Fragment)也能够通过处理来自onOptionsItemSelected()回调方法的点击事件来重写这个默认操作。如果你不在这个回调方法中处理点击事件,那么操作提供器会接收onPerformDefaultAction()回调来处理事件。但是,如果操作提供器提供了一个子菜单,那么Activity将不会接收onOptionsItemSelected()回调,因为子菜单的显示替代了选择时调用的默认菜单行为。

使用ShareActionProvider类

如果你想要在操作栏中提供一个“共享”操作,以充分利用安装在设备上的其他应用程序(如,把一张图片共享给消息或社交应用程序使用),那么使用ShareActionProvider类是一个有效的方法,而不是添加一个调用ACTION_SEND类型Intent对象的操作项。当你给一个操作项使用ShareActionProvider类时,它会呈现一个带有能够处理ACTION_SEND类型Intent对象的应用程序的下拉列表(如图8所示)。

http://developer.android.com/images/ui/actionbar-shareaction.png

图8. Gallery 应用截屏,用ShareActionProvider对象展开显示共享目标。
创建子菜单的所有逻辑,包括共享目标的封装、点击事件的处理(包在溢出菜单中的项目显示)等,都在ShareActionProvider类中实现了---你需要编写的唯一的代码是给对应的菜单项声明操作提供器,并指定共享的Intent对象。

默认情况,ShareActionProvider对象会基于用户的使用频率来保留共享目标的排列顺序。使用频率高的目标应用程序会显示在下来列表的上面,并且最常用的目标会作为默认共享目标直接显示在操作栏。默认情况下,排序信息被保存在由DEFAULT_SHARE_HISTORY_FILE_NAME指定名称的私有文件中。如果你只使用一种操作类型ShareActionProvider类或它的一个子类,那么你应该继续使用这个默认的历史文件,而不需要做任何事情。但是,如果你使用了不同类型的多个操作的ShareActionProvider类或它的一个子类,那么为了保持它们自己的历史,每种ShareActionProvider类都应该指定它们自己的历史文件。给每种ShareActionProvider类指定不同的历史文件,就要调用setShareHistoryFileName()方法,并且提供一个XML文件的名字(如,custom_share_history.xml)

注意:尽管ShareActionProvider类是基于使用频率来排列共享目标的,但是这种行为是可扩展的,并且ShareActionProvider类的扩展能够基于历史文件执行不同的行为和排序。

要添加ShareActionProvider对象,只需简单的给android.actionProviderClass属性设定android.widget.ShareActionProvider属性值就可以了。唯一要做的事情是定义你要用于共享的Intent对象,你必须先调用getActionProvider()方法来获取跟菜单项匹配的ShareActionProvider对象,然后调用setShareIntent()方法。

如果对于共享的Intent对象的格式依赖与被选择的菜单项,或其他的在Activity生存周期内改变的变量,那么你应该把ShareActionProvider对象保存在一个成员属性里,并在需要的时候调用setShareIntent()方法来更新它。如:

private ShareActionProvider mShareActionProvider;
...

@Override
public boolean onCreateOptionsMenu(Menu menu) {
mShareActionProvider = (ShareActionProvider) menu.findItem(R.id.menu_share).getActionProvider();

1
2
3
4
5
6
7
8
// If you use more than one ShareActionProvider, each for a different action,
// use the following line to specify a unique history file for each one.
// mShareActionProvider.setShareHistoryFileName("custom_share_history.xml");

// Set the default share intent
mShareActionProvider.setShareIntent(getDefaultShareIntent());

return true;

}
// When you need to update the share intent somewhere else in the app, call
// mShareActionProvider.setShareIntent()

上例中ShareActionProvider对象处理所有的跟这个菜单项有关的用户交互,并且不需要处理来自onOptionsItemSelected()回调方法的点击事件。

创建一个定制的操作提供器

当你想要创建一个有动态行为和在溢出菜单中有默认图标的操作视窗时,,继承ActionProvider类来定义这些行为是一个比好的的方案。创建自己的操作提供器,提供一个有组织的可重用的组件,而不是在Fragment或Activity的代码中处理各种操作项的变换和行为。

要创建自己的操作提供器,只需简单的继承ActionProvider类,并且实现合适的回调方法。你应该实现以下重要的回调方法:
ActionProvider()
::这个构造器把应用程序的Context对象传递个操作提供器,你应该把它保存在一个成员变量中,以便其他的回调方法使用。

OnCreateActionView()
::这是你给菜单项定义操作视窗的地方。使用从构造器中接收的Context对象,获取一个LayoutInflater对象的实例,并且用XML资源来填充操作视窗,然后注册事件监听器。如:

public View onCreateActionView() {
// Inflate the action view to be shown on the 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) {
// Do something...
}
});
return view;
}

onPerformDefaultAction()
::在选中溢出菜单中的菜单时,系统会调用这个方法,并且操作提供器应该这对这个选中的菜单项执行默认的操作。

但是,如果你的操作提供器提供了一个子菜单,即使是溢出菜单中一个菜单项的子菜单,那么也要通过onPrepareSubMenu()回调方法来显示子菜单。这样onPerformDefaultAction()在子菜单显示时就不会被调用。

注意:实现了onOptionsItemSelected()回调方法的Activity或Frament对象能够通过处理item-selected事件(并且返回true)来覆盖操作提供器的默认行为,这种情况下,系统不会调用onPerformDefaultAction()回调方法。

添加导航选项标签

当你想要在一个Activity中提供导航选择标签时,使用操作栏的选项标签是一个非常好的选择(而不是使用TabWidget类),因为系统会调整操作栏选项标签来适应不同尺寸的屏幕的需要---在屏幕足够宽的时候,导航选项标签会被放到主操作栏中;当屏幕太窄的时候,选项标签会被放到一个分离的横条中,如图9和图10所示。
http://developer.android.com/images/ui/actionbar.png

图9. Honeycomb Gallery应用程序中的操作栏选项标签的截图

http://developer.android.com/images/ui/actionbar-stacked.png

图10. 在窄屏设备上被堆放在操作栏中的选项标签的截屏

要使用选项标签在Fragmengt之间切换,你必须在每次选择一个选项标签时执行一个Fragment事务。如果你不熟悉如何使用FragmentTransaction对象来改变Fragment,请阅读Fragment开发指南。

首先,你的布局必须包含一个用于放置跟每个Fragment对象关联的选项标签的ViewGroup对象。并且要确保这个ViewGroup对象有一个资源ID,以便你能够在选项标签的切换代码中能够引用它。另外,如果选项标签的内容填充在Activity的布局中(不包括操作栏),那么Activity不需要任何布局(你甚至不需要调用setContentView()方法)。相反,你能够把每个Fragment对象放到默认的根ViewGroup对象中,你能够用android.R.id.content ID来引用这个ViewGroup对象(在Fragment执行事务期间,你能够在下面的示例代码中看到如何使用这个ID的。

决定了Fragment对象在布局中的显示位置后,添加选项标签的基本过程如下:

1.实现ActionBar.TabListener接口。这个接口中回调方法会响应选项标签上的用户事件,以便你能够切换Fragment对象;

2.对于每个要添加的选项标签,都要实例化一个ActionBar.Tab对象,并且调用setTabListener()方法设置ActionBar.Tab对象的事件监听器。还可以用setText()或setIcon()方法来设置选项标签的标题或图标。

3.通过调用addTab()方法,把每个选项标签添加到操作栏。

在查看ActionBar.TabListener接口时,注意到回调方法只提供了被选择的ActionBar.Tab对象和执行Fragment对象事务的FragmentTransaction对象---没有说明任何有关Fragment切换的事。因此。你必须定义自己的每个ActionBar.Tab之间的关联,以及ActionBar.Tab所代表的适合的Fragment对象(为了执行合适的Fragment事务)。依赖你的设计,会有几种不同的方法来定义这种关联。在下面的例子中,ActionBar.TabListener接口的实现提供了一个构造器,这样每个新的选项标签都会使用它自己的监听器实例。每个监听器实例都定义了几个在对应Fragment对象上执行事务时必须的几个成员变量。

例如,以下示例是ActionBar.TabListener接口的一种实现,在这个实现中,每个选项标签都使用了它自己的监听器实例:

public static class TabListener implements ActionBar.TabListener {
private Fragment mFragment;
private final Activity mActivity;
private final String mTag;
private final Class mClass;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/* *  Constructor used each time a new tab is created.
  *  @param activity  The host Activity, used to instantiate the fragment
  *  @param tag  The identifier tag for the fragment
  *  @param clz  The fragment's Class, used to instantiate the fragment
  * /
public TabListener(Activity activity, String tag, Class<T> clz) {
    mActivity = activity;
    mTag = tag;
    mClass = clz;
}

/*  The following are each of the ActionBar.TabListener callbacks * /

public void onTabSelected(Tab tab, FragmentTransaction ft) {
    // Check if the fragment is already initialized
    if (mFragment == null) {
        // If not, instantiate and add it to the activity
        mFragment = Fragment.instantiate(mActivity, mClass.getName());
        ft.add(android.R.id.content, mFragment, mTag);
    } else {
        // If it exists, simply attach it in order to show it
        ft.attach(mFragment);
    }
}

public void onTabUnselected(Tab tab, FragmentTransaction ft) {
    if (mFragment != null) {
        // Detach the fragment, because another one is being attached
        ft.detach(mFragment);
    }
}

public void onTabReselected(Tab tab, FragmentTransaction ft) {
    // User selected the already selected tab. Usually do nothing.
}

}

警告:针对每个回调中的Fragment事务,你都不必调用commit()方法---系统会调用这个方法,并且如果你自己调用了这个方法,有可能会抛出一个异常。你也不能把这些Fragment事务添加到回退堆栈中。

在这个例子中,当对应的选项标签被选择时,监听器只是简单的把一个Fragment对象附加(attach()方法)到Activity布局上---或者,如果没有实例化,就会创建这个Fragment对象,并且把它添加(add()方法)到布局中(android.R.id.content ViewGroup的一个子类),当这个选项标签解除选择时,对应的Fragment对象也会被解除与布局的依附关系。

ActionBar.TabListener的实现做了大量的工作,剩下的事情就是创建每个ActionBar.Tab对象并把它添加到ActionBar对象中,另外,你必须调用setNavigationMode(NAVIGATION_MODE_TABS)方法来让选项标签可见。如果选项标签的标题实际指示了当前的View对象,你也可以通过调用setDisplayShowTitleEnabled(false)方法来禁用Activity的标题。

例如,下面的代码使用上面定义的监听器在操作栏中添加了两个选项标签。

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Notice that setContentView() is not used, because we use the root
// android.R.id.content as the container for each fragment

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// setup action bar for tabs
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应用中你能够看到更多的这种样式。
http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentTabs.html

如果Activity终止了,那么你应该保存当前选择的选项标签的状态,以便当用户再次返回时,你能够打开合适的选项标签。在保存状态的时刻,你能够用getSelectedNavigationIndex()方法查询当前的被选择的选项标签。这个方法返回被选择的选项标签的索引位置。

警告:保存每个Fragment所必须的状态是至关重要的,因为当用户用选项标签在Fragment对象间切换时,它会查看Fragment在离开时样子。

注意:在某些情况下,Android系统会把操作栏选项标签作为一个下拉列表来显示,以便确保操作栏的最优化显示。

添加下拉式导航

作为Activity内部的另一种导航(或过滤)模式,操作栏提供了内置的下拉列表。下拉列表能够提供Activity中内容的不同排序模式。

启用下拉式导航的基本过程如下:
1. 创建一个给下拉提供可选项目的列表,以及描画列表项目时所使用的布局;
2. 实现ActionBar.OnNavigationListener回调,在这个回调中定义当用户选择列表中一个项目时所发生的行为;
3. 用setNavigationMode()方法该操作栏启用导航模式,如:

ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);


Note: You should perform this during your activity's onCreate() method.

4. 用setListNavigationCallbacks()方法给下拉列表设置回调方法,如:

actionBar.setListNavigationCallbacks(mSpinnerAdapter, mNavigationCallback);

这个方法需要SpinnerAdapter和ActionBar.OnNavigationListener对象。

That's the basic setup. However, implementing the SpinnerAdapter and ActionBar.OnNavigationListener is where most of the work is done. There are many ways you can implement these to define the functionality for your drop-down navigation and implementing various types of SpinnerAdapter is beyond the scope of this document (you should refer to the SpinnerAdapter class reference for more information). However, below is a simple example for a SpinnerAdapter and ActionBar.OnNavigationListener to get you started (click the title to reveal the sample).

Example SpinnerAdapter and OnNavigationListener

设置操作栏的样式

如果你对应用程序中的可视构件进行了定制化的设计,那么你可能也会要对操作栏做一些重新设计,以便跟应用程序的设计匹配。要这样做的话,需要使用Android的样式与主题框架中的一些特殊的样式属性来重新设置操作栏的样式。

注意:改变外观的背景图片依赖与当前按钮的状态(选择、按下、解除选择),因此你使用的可描画的资源必须是一个可描画的状态列表。

警告:对于你提供的所有可描画的背景,要确保使用NinePatch类型可描画资源,以便允许图片的拉伸。NinePatch类型的图片应该比40像素高30像素宽的图片要小。

普通的外观

android:windowActionBarOverlay
:这个属性声明了操作栏是否应该覆盖Activity布局,而不是相对Activity的布局位置的偏移。这个属性的默认值是false。通常,在屏幕上,操作栏需要它自己的空间,并且把剩下的空间用来填充Activity的布局。当操作栏四覆盖模式时,Activity会使用所有的有效空间,系统会在Activity的上面描画操作栏。如果你想要在操作栏隐藏和显示时,布局中的内容保持固定的尺寸好位置,那么这种覆盖模式是有用的。你也可能只是为了显示效果来使用它,因为你可以给操作栏设置半透明的背景,以便用户依然能够看到操作栏背后的Activity布局。

注意:默认情况下,Holo主题会用半透明背景来描画操作栏。但是,你能够用自己的样式来修改它,并且默认的情况下,DeviceDefault主题在不同的设备上可能使用不透明的背景。

覆盖模式被启用时,Activity布局不会感知到操作栏覆盖在它的上面,因此,在操作栏覆盖的区域,最好不要放置一些重要的信息或UI组件。如果适合,你能够引用平台的actionBarSize值来决定操作栏的高度,例如,在XML布局文件中引用这个值。

你还能够用getHeight()方法在运行时获取操作栏的高度。如果在Activity生存周期的早期调用这个方法,那么在调用时所反映的操作栏的高度可能不包括被堆放的操作栏(因为导航选项标签)。要看如何在运行时判断操作栏总的高度(包括被堆放的操作栏),请看Honeycomb Gallery示例应用中的TitlesFragment类。
http://developer.android.com/resources/samples/HoneycombGallery/index.html

操作项样式

android:actionButtonStyle
::给操作项按钮定义样式资源。
android:actionBarItemBackground
::给每个操作项的背景定义可描画资源(被添加在API 级别 14中)。
android:itemBackground
::给每个溢出菜单项的背景定义可描画资源。
android:actionBarDivider
::给操作项之间的分隔线定义可描画资源(被添加在API 级别 14中)
android:actionMenuTextColor
::给显示在操作项中文本定义颜色。
android:actionMenuTextAppearance
::给显示在操作项中文本定义样式资源。
android:actionBarWidgetThem
::给作为操作视窗被填充到操作栏中的可视构件定义主题资源(被添加在API级别14中)。

导航选项标签样式

android:actionBarTabStyle
::给操作栏中的选项标签定义样式资源。
android:actionBarTabBarStyle
::给显示在导航选项标签下方的细条定义样式资源。
android:actionBarTabTextStyle
::给导航选项标签中的文本定义样式资源。

下拉列表样式

android:actionDropDownStyle
给下拉导航列表定义样式(如背景和文本样式)。
如,下例XML文件中给操作栏定义了一些定制的样式:

<?xml version"utf-8"?>

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

@style/CustomTabTextStyle
@drawable/ab_divider
@drawable/ab_item_background

1
2
3
4
<!-- style for the action bar tab text -->
<style name"@android:style/TextAppearance.Holo">
    <item name="android:textColor">#2456c2</item>
</style>

注意:一定要在标签中声明一个父主题,这样定制的主题可以继承所有没有明确声明的样式。在修改操作栏样式时,使用父主题是至关重要的,它会让你能够简单的覆写你想要改变的操作栏样式,而不影响你不想修改的样式(如文本的外观或操作项的边缘)。

你能够在清单文件中把定制的主题应用到整个应用程序或一个单独的Activity对象,如:

高级样式

如果需要比上述属性更高级的样式,可以在Activity的主题中包含android:actionBarStyle和android:actionBarSplitStyle属性。这两个属性的每一个都指定了另一种能够给操作栏定义各种属性的样式,包括带有android:background、android:backgroundSplit、android:backgroundStacked属性的不同背景。如果要覆盖这些操作栏样式,就要确保定义一个像Widget.Holo.ActionBar这样的父操作栏样式。

例如,如果要改变操作栏背景,你可以使用下列样式:

<?xml version"utf-8"?>

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

@style/MyActionBar
<!-- other activity and action bar styles here -->

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

@drawable/ab_background
@drawable/ab_background
@drawable/ab_split_background

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics