从零开始--系统深入学习android(实践-让我们开始写代码-Android框架学习-4.Action Bar)

第4章 Action Bar

Action Bar是一个能用于确定应用程序和用户的位置,并提供给用户操作和导航模式的窗口功能。如果需要显著地展示当前用户的操作或导航,应该使用Action Bar,因为Action Bar为用户提供了一个统一的跨应用程序和系统的接口,并且针对不同尺寸的屏幕优雅的处理了Action Bar的适配。你可以通过ActionBar API来控制它的行为和可视性,这些API添加于Android 3.0(API级别为11)。 Action Bar设计的目的是:

◆提供一个专门的空间来确定应用程序的标识和用户的位置。

这是在应用程序图标或者是左侧的logo以及Activity的标题帮助下完成的。如果当前视图的导航标签被标识,例如当前选项卡选中,你可能会选择删除该Activity名称。

◆提供统一的导航和视图细化到不同的应用程序中。

Action Bar提供了内置选项卡导航来进行在fragments之间切换。它还提供了一个下拉列表中,可以来用来替代导航模式或用来完善当前视图(比如按照不同的标准来排序列表)。

◆突出Activity的关键动作(如“搜索”、“创建”、“共享”,等等。),便于用户一个可预测的访问。

对于关键的用户操作,你可以通过将item从选项菜单直接在操作栏定义为action items来提高访问速度。action items也可以提供一个“action view”,它用一个嵌入式widget来提供更多及时的动作行为。没有晋升为成action items的菜单项在溢出菜单中还是有效的,用户既可以使用设备上的菜单按钮(设备上有按钮的时候),也可以使用Action Bar中的溢出菜单按钮(当设备上不包含菜单按钮时)。如图4-1所示:

 

图4-1从左边开始,依次为logo,导航标签与action item和最右边的溢出菜单按钮。

4.1 添加Action Bar

从Android3.0(API level 11)开始,Action Bar包括在所有Activity中使用的Theme.Holo主题(或是继承Activity的一个子类),这是当targetSdkVersion或minSdkVersion属性设置为“11”或更高时程序默认的主题。如代码清单4-1所示:

<manifest ... >



    <uses-sdk android:minSdkVersion="4"



              android:targetSdkVersion="11" />



    ...



</manifest>

 

代码清单4-1

在这个例子中,应用程序设置的最低版本的API Level为4(Android 1.6),但它目标API级别为11(Android 3.0)。通过这样设置,当应用程序运行在Android 3.0或更高版本上时,系统为每个Activity应用Holo主题,因此,每一个Activity都包含Action Bar。如果你想使用Action Bar的API,比如添加导航模式和修改操作栏样式,你应该设置minSdkVersion为“11”或是更高的版本。如果你想你的应用程序支持旧版本的Android,有很多办法可以让低版本的ActionBar的API的在支持API级别为11或更高的设备上,同时仍运行旧版本。

4.1.1移除Action Bar

如果你不想为一个特定的Activity设置Action Bar,那么你可以设置Activity主题为Theme.Holo.NoActionBar。如代码清单4-2所示:

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

 

代码清单4-2

您也能在运行时通过调用hide()隐藏Action Bar。如代码清单4-3所示:

ActionBar actionBar = getActionBar();



actionBar.hide();

 

代码清单4-3

当Action Bar隐藏时,系统会调整Activity的布局来填充所有可用屏幕空间。当然你可以通过调用show()显示Action Bar。 隐藏和删除Action Bar可能会使Activity重新调整布局。如果你的Activity经常隐藏和显示Action Bar(如在Android的画廊应用),你可能想用覆盖(Overlay)模式。覆盖模式布局在Activity的上层,而不是在同一层下的Activity的顶部。这样,你的布局可以在Action Bar隐藏和重新出现时保持不变。要启用覆盖模式,创建Activity主题并且将android:windowActionBarOverlay属性值设置为true。注意:如果你有一个自定义的的Activity主题并且想在其中删除Action Bar,然后把android:windowActionBar样式属性设置为false。,那么,由于你移除了Action Bar正在使用的主题,那么getActionBar()方法将返回null,当你和系统调用此方法的时候就会出现问题。

4.2 添加Action Items

有时你可能想让用户从选项菜单(options menu)中直接访问item。要做到这一点,你可以声明该菜单项为Action Bar中的一个“action item”。一个“action item”包括一个图标和/或文字标题。如果一个菜单项不作为一个“action item”,系统会将菜单项放置在溢出菜单。溢出菜单显示设备菜单“按钮(如果设备提供)或在操作栏中的按钮(如果设备不提供”菜单“按钮)。首次启动Activity时,系统通过在activity调用onCreateOptionsMenu()方法来填充action bar和溢出菜单(overflow menu)。在菜单开发指南中讨论的,它是在这个回调方法,你应该在inflate一个XML定义菜单项的菜单资源。如代码清单4-4和图4-2所示:

@Override



public boolean onCreateOptionsMenu(Menu menu) {



    MenuInflater inflater = getMenuInflater();



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



    return true;



}

 

代码清单4-4

 

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

在XML文件中,你可以通过声明android:showAsAction="ifRoom" 让菜单item成为action item。通过这种方式,当有可用空间时,菜单项才会出现在action item的快速访问栏。如果没有足够的空间,该item将出现在溢出菜单。如果你的菜单项同时提供标题和图标--同时具有Android:titile和android:icon属性,action item默认只会显示图标。但如果你想显示文字标题,必须添加“withText”到Android:showAsAction属性中。如代码清单4-5所示:

<?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>

 

代码清单4-5

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

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

◆如果在action bar中没有足够的空间提供给action item,该菜单项出现溢出“菜单中,而且只有标题显示。

◆屏幕阅读器为视障用户读出菜单项的标题。

◆如果action item只有图标,用户可以长按item显示的工具提示,显示action item的标题。

Android的图标始终是可选的。

你也可以定义一个item“always”为action item,以避免当空间有限时被放到溢出菜单中去。但在大多数情况下,不应该设置“always”这个值来强制使一个item出现在action bar中。要注意的是过多的action item,会导致创建出来的UI杂乱不堪,并且在窄屏幕的设备上会出现布局问题。最好使用“ifRoom”,在没有足够的空间时,应允许系统将它移动到溢出菜单。

4.2.1选项你的Action items

你应该应通过评估的几个关键特性,仔细从选项菜单中选出action item。在一般情况下,每个action item,至少有以下需求之一:

1、常用性:用户70%的时间需要访问或需要连续多次使用。常用性例子:在短信息应用程序的“新信息”和Google Player中的“搜索(Search)”。

2、重要性:用户能够很容易地发现,或者如果不经常使用,在少数情况用户确实需要它的时候,可以毫不费力地执行,这一点是很重要的。重要性例子:在 Wi-Fi设置“加入网络”和在画廊(Gallery)应用程序中“切换到相机”。

3、典型性:这是一个通常在类似的应用程序的action bar中提供的action,因此,用户希望自己找到它。典型性例子:电子邮件应用中的“刷新”和电话本应用中的“新的联系人”。

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

4.2.2使用分离(split)的Action Bar

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

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

 

图4-3 左侧使用的导航标签的split action bar与应右侧禁用程序图标和标题的效果对比。

4.3 使用应用程序图标来导航

默认情况下,应用程序图标显示在action bar的左边。你能够把这个图标当做action item来使用。应用程序应该在这个图标上响应以下两个操作之一:

返回应用程序的“主”Activity;

◆    返回“home”界面。

◆    向应用程序上层结构返回。

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

如果你要通过应用程序图标的响应来返回home屏幕,那么就应该在Intent对象中包含FLAG_ACTIVITY_CLEAR_TOP标识。用这个标志,如果你要启动的Activity在当前任务中已经存在,那么,堆栈中这个Activity之上的所有的Activity都有被销毁,并且把这个Activity显示给用户。添加这个标志往往是重要的,因为返回home屏幕相当与一个回退的动作,而不是创建。

例如,下例的onOptionsItemSelected()方法实现了返回应用程序的home屏幕的操作,如代码清单4-6所示:

@Override



public boolean onOptionsItemSelected(MenuItem item) {



    switch (item.getItemId()) {



        case android.R.id.home:



            // 在action bar点击app icon; 回到 home



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



            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);



            startActivity(intent);



            return true;



        default:



            return super.onOptionsItemSelected(item);



    }



}

 

代码清单4-6

在用户从另一个应用程序进入当前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对象添加FLAG_ACTIVITY_NEW_TASK标识,如代码清单4-7所示:

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

 

代码清单4-7

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

4.3.1应用程序向上导航

作为传统的回退导航(把用户带回任务历史中的前一个窗口)的补充,你能够让action bar图标提供向上导航的功能,它应用把用户带回到你的应用程序的上级界面。例如,当前界面时你的应用程序层次比较深的一个界面,点击应用程序图标应该返回返回上一级页面(当前界面的父界面)。如图4-4所示:

 

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

例如,图4-5演示了当用户从一个应用程序导航到一个属于不同应用程序的Activity时,“回退”按钮的行为。

 

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

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

 

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

要是应用程序图标能够向上导航,就要在你的ActionBar中调用

SetDisplayHomeAsUpEnabledtrue(true)方法,如代码清单4-8所示:

protected void onCreate(Bundle savedInstanceState) {



    super.onCreate(savedInstanceState);



 



    setContentView(R.layout.main);



    ActionBar actionBar = getActionBar();



    actionBar.setDisplayHomeAsUpEnabled(true);



    ...



}

 

代码清单4-8

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

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

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

4.4 添加Action View

Action View是作为action item按钮的替代品显示在action bar中的一个widget。例如,如果你有一个用于搜索的可选菜单项,你可以用SearchView widget来替代action ba的搜索按钮,如图4-7所示:

 

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

要在菜单资源中的一个item声明一个action view,你既可以使用android:actionLayout属性也android:actionViewClass属性来分别指定一个布局资源或要使用的可视构件类。如代码清单4-9所示:

<?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>

 

代码清单4-9

android:showAsAction属性也可包含“collapseActionView”属性值,这个值是可选的,并且声明了这个action view应该被折叠到一个按钮中,当用户选择这个按钮时,这个action view展开。否则,这个action view在默认的情况下是可见的,并且即便在用户不用的时候,也要占据action bar的有效空间。如果需要给action view添加一些事件,那么就需要在onCreateOptionsMenu()回调期间做点事。你能够通过调用带有菜单项ID的findItem()方法来获取菜单项,然后再调用getActionView()获取该元素。例如,使用以下方法获取上例中的搜索视窗widget,如代码清单4-10所示:

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



}

 

代码清单4-10

4.4.1处理可折叠的action views

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

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

当用户选择了action bar中的“向上”图标或按下了回退按钮时,系统也会把action view折叠起来。如果需要,你能够在代码中通过在expandActionView()和collapseActionView()方法来展开或折叠action view。

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

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

@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;          }



 



        @Override



        public boolean onMenuItemActionExpand(MenuItem item) {



            return true; 



        }



    });



}

 

代码清单4-11

4.5 添加Action Provider

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

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

要给一个操作项声明一个操作提供器,就要在菜单资源中对应的<item>元素中定义android:actionProviderClass属性,提供器要使用完整的类名。如代码清单4-12所示:

<?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>

 

代码清单4-12

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

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

4.5.1使用ShareActionProvider类

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

 

图4-8 Gallery应用截屏,用ShareActionProvider对象展开显示共享目标。

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

默认情况,ShareActionProvider对象会基于用户的使用频率来保留共享目标的排列顺序。使用频率高的目标应用程序会显示在下来列表的上面,并且最常用的目标会作为默认共享目标直接显示在action bar。默认情况下,排序信息被保存在由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()方法来更新它。如代码清单4-13所示:

private ShareActionProvider mShareActionProvider;



...



 



@Override



public boolean onCreateOptionsMenu(Menu menu) {



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



    mShareActionProvider.setShareIntent(getDefaultShareIntent());



 



    return true;



}

 

代码清单4-13

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

4.5.2创建自定义的Action Provider

当你想要创建一个有动态行为和在溢出菜单中有默认图标的action view时,,继承ActionProvider类来定义这些行为是一个比好的的方案。创建自己的action provider,提供一个有组织的可重用的组件,而不是在Fragment或Activity的代码中处理各种action item的变换和行为。要创建自己的action provider,只需简单的继承ActionProvider类,并且实现合适的回调方法。你应该实现以下重要的回调方法:

ActionProvider()

这个构造器把应用程序的Context对象传递个操作提供器,你应该把它保存在一个成员变量中,以便其他的回调方法使用。

OnCreateActionView()

这是你给菜单项定义action view的地方。使用从构造器中接收的Context对象,获取一个LayoutInflater对象的实例,并且用XML资源来填充action view,然后注册事件监听器。如代码清单4-14所示:

public View onCreateActionView() {



    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;



}

 

代码清单4-14

onPerformDefaultAction()

在选中溢出菜单中的菜单时,系统会调用这个方法,并且action provider应该这对这个选中的菜单项执行默认的操作。但是,如果你的action provider提供了一个子菜单,即使是溢出菜单中一个菜单项的子菜单,那么也要通过onPrepareSubMenu()回调方法来显示子菜单。这样onPerformDefaultAction()在子菜单显示时就不会被调用。注意:实现了onOptionsItemSelected()回调方法的Activity或Frament对象能够通过处理item-selected事件(并且返回true)来覆盖action provider的默认行为,这种情况下,系统不会调用onPerformDefaultAction()回调方法。

4.6 添加导航标签

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

 

图4-9 android3.0下Gallery应用程序中的操作栏选项标签的截图

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

要使用选项标签在Fragmengt之间切换,你必须在每次选择一个选项标签时执行一个Fragment事务处理。如果你不熟悉如何使用FragmentTransaction对象来改变Fragment,请阅读先前Fragment章节。首先,你的布局必须包含一个用于放置跟每个Fragment对象关联的选项标签的ViewGroup对象。并且要确保这个ViewGroup对象有一个资源ID,以便你能够在选项标签的切换代码中能够引用它。另外,如果选项标签的内容填充在Activity的布局中(不包括action bar),那么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接口的一种实现,在这个实现中,每个选项标签都使用了它自己的监听器实例,如代码清单4-15所示:

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;



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



        mActivity = activity;



        mTag = tag;



        mClass = clz;



    }



 



    public void onTabSelected(Tab tab, FragmentTransaction ft) {   



        if (mFragment == null) {



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



            ft.detach(mFragment);



        }



    }



 



    public void onTabReselected(Tab tab, FragmentTransaction ft) {



    }



}

 

代码清单4-15

注意:针对每个回调中的Fragment事务处理,你都不必调用FragmentTransaction.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的标题。

例如,下面的代码使用上面定义的监听器在action bar中添加了两个选项标签。如代码清单4-16所示:

@Override



protected void onCreate(Bundle savedInstanceState) {



    super.onCreate(savedInstanceState);



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



}

 

代码清单4-16

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

注意:在某些情况下,Android系统会把action bar选项标签作为一个下拉列表来显示,以便确保操作栏的最优化显示。并且保存每个Fragment所必须的状态是至关重要的,因为当用户用选项标签在Fragment对象间切换时,它会查看Fragment在离开时样子。

4.7 添加下拉式导航

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

启用下拉式导航的基本过程如下:

1. 创建一个给下拉提供可选项目的列表,以及描画列表项目时所使用的布局;

2. 实现ActionBar.OnNavigationListener回调,在这个回调中定义当用户选择列表中一个项目时所发生的行为;

3. 用setNavigationMode()方法该操作栏启用导航模式,如代码清单4-17所示:

ActionBar actionBar = getActionBar();



actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);

 

代码清单4-17

注意: 你应该在activity的onCreate()方法中执行以上代码

4. 用setListNavigationCallbacks()方法给下拉列表设置回调方法,如代码清单4-18所示:

actionBar.setListNavigationCallbacks(mSpinnerAdapter, mNavigationCallback);

 

代码清单4-18

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

以上是基本的设置。当然实现了SpinnerAdapter和ActionBar.OnNavigationListener就已经完成了大部分工作。有许多方法能实现这些定义下拉列表的功能并且实现,这已经超出了本章的范围,就不赘述了。

4.8 Action Bar的样式(Sytle)

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

注意:改变外观的背景图片依赖与当前按钮的状态(选择、按下、解除选择),因此需要使用state list drawable。还要确保使用NinePatch类型的drawable资源,以便允许图片的拉伸。NinePatch类型的图片应该比40像素高30像素宽的图片要小(mdpi下)。

4.8.1普通的外观

android:windowActionBarOverlay

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

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

覆盖模式被启用时,Activity布局不会感知到action bar覆盖在它的上面,因此,在action bar覆盖的区域,最好不要放置一些重要的信息或UI组件。适当的情况下,你能够引用平台的actionBarSize值来决定操作栏的高度,例如,在XML布局文件中引用这个值,如代码清单4-19所示:

<SomeView



    ...



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

 

代码清单4-19

你还能够用getHeight()方法在运行时获取action bar的高度。如果在Activity生存周期的早期调用这个方法,那么在调用时所反映的action bar的高度可能不包括被堆放的action bar(因为导航选项标签)。要看如何在运行时判断action bar总的高度,请看simple中Honeycomb Gallery的TitlesFragment类

4.8.2 Action Items

android:actionButtonStyle

给操作项按钮定义样式资源。

android:actionBarItemBackground

给每个操作项的背景定义可描画资源(被添加在API Level 14中)。

android:itemBackground

给每个溢出菜单项的背景定义可描画资源。

android:actionBarDivider

给操作项之间的分隔线定义可描画资源(被添加在API Level 14中)

android:actionMenuTextColor

给显示在操作项中文本定义颜色。

android:actionMenuTextAppearance

给显示在操作项中文本定义样式资源。

android:actionBarWidgetThem

给作为操作视窗被填充到操作栏中的可视构件定义主题资源(被添加在API Level 14中)。

4.8.3导航标签

android:actionBarTabStyle

给操作栏中的选项标签定义样式资源。

android:actionBarTabBarStyle

给显示在导航选项标签下方的细条定义样式资源。

android:actionBarTabTextStyle

给导航选项标签中的文本定义样式资源。

4.8.4下拉列表

android:actionDropDownStyle

给下拉导航列表定义样式(如背景和文本样式)。如,下例XML文件中给action bar定义了一些定制的样式,如代码清单4-20所示:

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



<resources>



    <!--应用程序或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>



 



    <!--  action bar标签文本样式-->



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



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



    </style>



</resources>

 

代码清单4-20

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

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

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

 

代码清单4-21

4.8.5高级样式

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

例如,如果要改变操作栏背景,你可以使用下列样式,如代码清单4-22所示:

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



<resources>



    <!-- 应用程序或activity应用的主题 -->



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



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



         </style>



 



    <!-- 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>

 

代码清单4-22

 讨论QQ群:213821767

你可能感兴趣的:(android)