目录导航

自定义操作栏

使用操作栏作为应用程序的导航

使用Android菜单系统

选择操作栏的操作

创建身临其境的应用程序

创建和显示对话框

显示Toast

使用Notification Manager通知用户应用程序事件

创建连续的和不间断的Notification

本文着眼于在你设计的UI元素以外来扩展用户体验的方式

首先介绍Android 3.0中引入的操作栏,它是一个系统级的UI控件,用来在Activity中为品牌打造、导航和显示常规的操作提供一个一致性的模式。你将学习如何自定义操作栏的外观,同时学习如何利用Tab键和下拉列表来提供导航功能。

操作栏的操作、应用程序菜单和弹出式菜单是访问菜单的新方法,并针对现代的触屏设备进行了优化。作为Android UI模型检查的一部分,本章着眼于如何在你的应用程序中创建和使用它们。特别地,你将学习如何确定操作栏上哪个菜单项应该作为一个操作来显示。

在没有Activity的情况下,Android也为应用程序提供了一些技术来和用户进行通信。你将学习在不打断处于活动状态的应用程序的情况下,如何使用NotificationToast来警示和更新用户。

Toast是一个短暂的、非模态的对话框机制,用来在不获取当前活动的应用程序焦点的情况下向用户展示信息。你将学习在任意的应用程序组件上显示Toast,它会向用户发送一条在屏幕上显示的不显眼的消息。

Toast是静默而短暂的,Notification则代表一个更加健壮的机制来提醒用户。在许多情况下,当用户不使用手机时,手机会放在口袋里或桌子上,在没有响铃、震动或闪烁的时候,它都会保持安静。如果用户错过这些警示,状态栏的图标就会指示发生了事件。通过使用Notification,所有这些引人注目的事件对于Android应用程序都是可用的。

你 还将学习当Notification出现在通知托盘中时,如何自定义该Notification的外观和功能。通知托盘为用户提供了一种机制,能够在不需要先打开应用程序的情况下与该应用程序进行交互。

1 操作栏简介

如图1所示的操作栏组件是在Android 3.0(API level 11)中引入的。它是一个导航面板,代替了每个Activity上方的标题栏,并正式成为了一个通用的Android设计模式。

1

可以隐藏操作栏,但最好的方式是保留它并自定义,使它能够适合应用程序的样式和导航要求。

在应用程序中,操作栏可以添加到每个Activity中,它的作用是在应用程序之间和特定应用程序的Activity内提供一个一致性的UI外观。

操作栏为品牌打造、导航和在Activity内执行的关键操作提供了一致的框架。虽然操作栏为在应用程序间呈现这种一致性的功能提供了一个框架,但下面的章节还将描述如何选择哪些选项适合你的应用程序,以及应该如何实现它们。

如果任意的Activity使用了(默认的)Theme.Holo主题,并且它的应用程序的目标(或最小)SDK版本为11或者更高,那么它的操作栏是启用的。

程序清单1-1显示了在不修改默认主题的情况下,通过设置目标SDKAndroid 4.0.3(API level 15)启用操作栏。

程序清单1-1 启用操作栏

<uses-sdk android:targetSdkVersion="15" />

代码片段PA4AD_Ch10_ActionBar/AndroidManifest.java

要想在运行时设置操作栏的可见性,可以使用它的showhide方法:

ActionBar actionBar = getActionBar();

// 隐藏操作栏

actionBar.hide();

// 显示操作栏

actionBar.show();

作为一种选择,也可以应用一个不包含操作栏的主题,例如Theme.Holo.NoActionBar主题,如程序清单10-2所示。

程序清单1-2 禁用操作栏

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


代码片段PA4AD_Ch10_ActionBar/AndroidManifest.java

通过设置android:windowActionBar的样式属性为false,可以创建或者自定义移除操作栏的主题。

<?xml version="1.0" encoding="utf-8"?><resources>  <style name="NoActionBar" parent="@style/ActivityTheme">    <item name="android:windowActionBar">false</item>  </style></resources>

当一个Activity应用了一个不含有操作栏的主题后,将不能通过程序在运行时显示它,调用getActionBar会返回 null

操作栏是Android 3.0 (API level 11)中引入的,在现有的支持库中还没有包含它。因此,只能在最低Android 3.0的主机平台上运行的应用程序中才能使用操作栏。在运行Android 3.0之前的平台中,一种选择是创建不同的布局。这种布局需要实现自己的自定义操作栏——通常是以Fragment的形式——以提供类似的功能。

1.1.1 自定义操作栏

除了能够控制这个标准功能的实现,每个应用程序还可以修改操作栏的外观,同时保持相同的一致性行为和常规的布局。

操作栏的一个主要的目的就是提供应用程序间统一的UI。因此,尽管可以自定义操作栏来提供自己应用程序的商标和标识,但这种自定义选项行为还是刻意有所限制的。

通过指定图像(如果有的话)出现在最左边,可以控制品牌打造,此外还可以控制应用程序的标题显示以及背景Drawable的使用。图1-2显示了自定义的操作栏,使用徽标位图来标识应用程序以及使用一个渐变Drawable作为背景图像。

1-2

1. 修改图标和标题文本

默认情况下,操作栏是通过使用应用程序或者Activity中指定的android:icon属性来显示Drawable的,旁边则是黑色背景上相应的android:label属性。

可以使用android:logo属性指定一个可选的图形。和正方形的图标不同,对于徽标图形的宽度是没有限制的,但最好限制它的宽度大概为图标宽度的两倍。

徽标图像通常为应用程序提供顶层的品牌打造,因此在使用徽标图像的时候最好隐藏标题标签。可以通过在运行时设置setDisplayShowTitleEnabled 方法为false来实现这一点。

ActionBar actionBar = getActionBar();actionBar.setDisplayShowTitleEnabled(false);

在图标和徽标图像都提供的地方,可以通过使用setDisplayUseLogoEnabled方法在它们之间转换。

actionBar.setDisplayUseLogoEnabled(displayLogo);

如果选择隐藏图标和徽标,可以通过设置setDisplayShowHomeEnabledfalse来实现。

actionBar.setDisplayShowHomeEnabled(false);

应用程序的图标/徽标通常用作应用程序主Activity的导航快捷方式,因此最好设置它为可见。

还可以使用图标和标题文本来提供导航和上下文线索。在运行时,使用 setTitle setSubTitle 方法来修改图标旁边显示的文本,如程序清单1-3和图1-3所示。

程序清单1-3 自定义操作栏标题

actionBar.setSubtitle("Inbox");actionBar.setTitle("Label:important");


代码片段PA4AD_Ch10_ActionBar/src/ActionBarActivity.java

1-3

这些文本字符串用来描述用户在应用程序中的位置和他们工作时的上下文情况。这在使用Fragment而非传统的Activity栈方式来改变上下文时是非常有用的。下面的小节将提供更多和导航选项相关的细节。

2. 自定义背景

操作栏默认的背景颜色取决于底层的主题。Android原生的操作栏背景是透明的,使用Holo主题后的背景颜色为黑色。

使用setBackgroundDrawable方法,可以指定任意Drawable作为操作栏的背景图像,如程序清单1-4所示。

程序清单1-4 自定义操作栏背景

ActionBar actionBar = getActionBar();Resources r = getResources(); Drawable myDrawable = r.getDrawable(R.drawable.gradient_header); actionBar.setBackgroundDrawable(myDrawable);


代码片段PA4AD_Ch10_ActionBar/src/ActionBarActivity.java

操作栏会拉伸你提供的图像,因此最好是创建一个可拉伸的Drawable,通常使用通过9-patch或者XML定义的Drawable

通常情况下,操作栏会在Activity的顶部保留一部分空间,Activity布局则会填充到剩余的空间中。作为一种选择,通过请求窗口特性为FEATURE_ACTION_BAR_OVERLAY,可以在Activity布局之上重叠显示操作栏。

[java] view plain copy print ?
  1. @Override
  2. public void onCreate(Bundle savedInstanceState) {
  3. super.onCreate(savedInstanceState);
  4. getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
  5. setContentView(R.layout.main);
  6. }
@Overridepublic void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);   getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);   setContentView(R.layout.main);}


当启用重叠模式时,操作栏会浮动在Activity之上,有可能掩盖布局顶部的内容。

3. 启用拆分操作栏模式

操作栏最初是在Android 3.0中引入的——这个平台版本专注于在平板设备上提供极佳的用户体验。Android 4.0(API level 14)则试图优化很多起初为平板电脑所设计的功能,使它们可以在更小的设备或者智能设备上使用。

对于操作栏来说,这就意味着拆分操作栏的引入。可以通过在应用程序或者Activity的清单节点中设置android:uiOptions属性值为splitActionBarWhenNarrow来启用拆分操作栏,如程序清单1-5所示。

程序清单1-5 启用拆分操作栏

<activity  android:label="My Activity"  android:name=".ActionBarActivity"  android:logo="@drawable/ic_launcher"  android:uiOptions="splitActionBarWhenNarrow">

代码片段PA4AD_Ch10_ActionBar/AndroidManifest.xml

在窄屏的支持设备(如竖屏模式下的智能手机)上,启用拆分操作栏模式可以让系统将操作栏拆分成多个独立的部分。图1-4显示了一个示例,带有品牌打造和导航部分的操作栏布局在屏幕的顶部,操作部分则与屏幕的底部对齐。

1-4

操作栏的布局是由运行时计算和执行的,并且可能根据宿主设备的方向和在运行时对操作栏进行的配置而改变。

1.1.2 自定义操作栏来控制应用程序的导航行为

操作栏引入了很多选项,用于在应用程序内部提供一致且可预见的导航功能。从广义来讲,这些选项可以分为两类:

应用程序图标 应用程序的图标或者徽标用来提供一种统一的导航路径,通常是通过将应用程序复位到它的主Activity。还可以配置图标,使其能够实现向“上”移动上下文的一个层次(即回到上一层的界面)

Tab键和下拉列表 操作栏支持内置Tab键或者下拉列表,用来代替Activity中可见的Fragment

图标导航可以认为是Activity栈的导航方法,而Tab键和下拉列表则用作Activity内的Fragment过渡。实际上,当应用程序图标被单击或者一个Tab键改变时所执行的操作依赖于实现UI的方式。

选择应用程序的图标应该像Activity切换一样改变UI的整体上下文,而改变Tab键或者选择一个下拉列表应该只改变显示的数据。

1. 配置操作栏图标的导航行为

大多数情况下,应用程序图标应该充当返回到“主”Activity的快捷方式,通常是回到Activity栈的根部。要想应用程序图标能够被单击,需要调用操作栏的setHomeButtonEnabled方法:

actionBar.setHomeButtonEnabled(true);

应用程序图标/徽标的单击由系统作为一个特殊的菜单项单击事件而广播。菜单项的选择是由Activity中的onOptionsItemSelected处理程序进行处理的,其中会把传入的菜单项参数的ID设置为android.R.id.home,如程序清单10-6所示。

创建和处理菜单项选择的过程会在本章以后的小节中详细介绍。

程序清单1-6 处理应用程序图标的单击事件


@Overridepublic boolean onOptionsItemSelected(MenuItem item) {  switch (item.getItemId()) {    case (android.R.id.home) :      Intent intent = new Intent(this, ActionBarActivity.class);      intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);      startActivity(intent);      return true;    default:      return super.onOptionsItemSelected(item);  }}


代码片段PA4AD_Ch10_ActionBar/src/ActionBarActivity.java

传统来说,Android应用程序启动一个新的Activity来进行不同上下文间的过渡。反过来,按下返回按钮会关闭活动Activity并且返回到上一个界面。

想要补充以上这种行为,可以配置应用程序图标以提供“向上”的导航功能。

返回按钮通常将用户从他们可见的上下文(通常以Activity的形式)中返回,有效地反转了到达当前Activity或屏幕所遵循的导航路径。这可能会导致实现应用程序结构中的“兄弟姐妹”界面间的导航

相反,“向上”导航通常将用户移到当前Activity的父界面。因此,它能将用户移到他们之前未访问过的界面。这对那些有多个入口点的应用程序尤其有用,允许用户在应用程序中导航,而无须返回到父应用程序。

要想启用应用程序图标的“向上”导航功能,可以调用操作栏的setDisplayHomeAsUpEnabled 方法。

actionBar.setDisplayUseLogoEnabled(false);

actionBar.setDisplayHomeAsUpEnabled(true);

这样会在应用程序图标上呈现一个重叠的“向上”图形,如图1-5所示。在希望启用向上导航的时候,最好使用图标而不是徽标。

1-5

导航的处理由你实现。操作栏依然会触发菜单项的onOptionsItemSelected处理程序,并使用android.R.id.home作为标识符,如程序清单1-6所示。

这种行为也引入了一定的危险,特别是当用户有多种方式访问一个特殊的Activity时。如果有顾虑的话,向上行为就应该和返回按钮的行为一致。在所有的情况下,导航行为应该是可预见的。

2. 使用导航Tab

除了应用程序图标导航,操作栏还提供了导航Tab键和下拉列表。注意,一次只可以启用一种导航形式。这些导航选项旨在与Fragment密切搭配使用,并提供一种机制,就是通过替换可见的Fragment达到改变当前Activity内容的目的。

导航Tab(如图1-6所示)可以被认为是TabWidget控件的一种替代。

1-6

要想配置操作栏以显示Tab键,需要调用它的setNavigationMode方法,并指定参数为ActionBar. NAVIGATION_MODE_TABSTab键应该提供应用程序的上下文信息,因此最好同样禁用标题文本,如程序清单10-7所示。

程序清单1-7 启用操作栏导航Tab

actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);actionBar.setDisplayShowTitleEnabled(false);


代码片段PA4AD_Ch10_ActionBar/src/ActionBarTabActivity.java

Tab键导航是通过Action BaraddTab方法添加到操作栏上的,如程序清单1-8所示。首先创建一个新的Tab并使用它的setTextsetIcon方法来设置要显示的标题和图像。另外,可以使用setCustomView方法,用你自定义的View代替操作栏标准的文本和图像布局。

程序清单1-8 添加操作栏导航Tab键

Tab tabOne = actionBar.newTab(); tabOne.setText("First Tab")      .setIcon(R.drawable.ic_launcher)      .setContentDescription("Tab the First")      .setTabListener(         new TabListener<MyFragment>           (this, R.id.fragmentContainer, MyFragment.class)); actionBar.addTab(tabOne);

代码片段PA4AD_Ch10_ActionBar/src/ActionBarTabActivity.java

Android 4.0(API level 14)引入了setContentDescription方法,它允许包含更加详细的内容描述来为可访问性提供更好的支持。

Tab键的切换是通过 TabListener来处理的,它允许创建 Fragment事务来响应 Tab键的选中、未选中和重新选中的操作,如程序清单 1-9所示。注意,你不需要执行在每个处理程序中创建的 Fragment事务——操作栏会在必要的时候为你执行它。

程序清单1-9 处理操作栏Tab键切换

public static class TabListener<T extends Fragment>  implements ActionBar.TabListener {   private MyFragment fragment;  private Activity activity;  private Class<T> fragmentClass;  private int fragmentContainer;   public TabListener(Activity activity, int fragmentContainer,                     Class<T> fragmentClass) {     this.activity = activity;    this.fragmentContainer = fragmentContainer;    this.fragmentClass = fragmentClass;  }
// 当一个新的Tab键被选中时调用

public void onTabSelected(Tab tab, FragmentTransaction ft) {    if (fragment == null) {      String fragmentName = fragmentClass.getName();      fragment =        (MyFragment)Fragment.instantiate(activity, fragmentName);      ft.add(fragmentContainer, fragment, null);      fragment.setFragmentText(tab.getText());    } else {      ft.attach(fragment);    }  }


// 当其他的Tab键被选中时,在当前选中的Tab键上调用

public void onTabReselected(Tab tab, FragmentTransaction ft) {    //TODO:当选中的Tab键被再次选中时响应  }}


代码片段PA4AD_Ch10_ActionBar/src/ActionBarTabActivity.java

在这个Tab Listener中,基本的工作流程是初始化、配置新的Fragment然后在onTabSelected处理程序中将此Fragment添加到布局中。在Tab键未选中时,它关联的Fragment应该从布局中分离,当其Tab键被重新选中时,该Fragment应该被回收利用。

3. 使用下拉列表导航

使用操作栏的下拉列表可以替代导航Tab键,如图10-7所示。在向Activity中显示的内容应用一个合适的过滤器时,它也是一种理想的解决方案。

1-7

要想设置操作栏以显示一个下拉列表,调用它的setNavigationMode方法,并指定参数为ActionBar.NAVIGATION_MODE_LIST

actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);

操作栏的下拉列表实现非常像Spinner——Spinner视图每次只显示一个子选项,并允许用户从众多的选项中选择这个子选项。如同Array Adapter或者Simple Cursor Adapter一样,可以通过创建一个新的实现了SpinnerAdapter接口的适配器来填充下拉列表:

ArrayList<CharSequence> al = new ArrayList<CharSequence>();al.add("Item 1");al.add("Item 2"); ArrayAdapter<CharSequence> dropDownAdapter =  new ArrayAdapter<CharSequence>(this,    android.R.layout.simple_list_item_1,    al);


要想将这个适配器分配给操作栏并处理选择事件,可以调用操作栏的setListNavigationCallbacks,并传入适配器和一个OnNavigationListener,如程序清单1-10所示。

程序清单1-10 创建一个操作栏下拉列表

// 选择下拉列表导航模式。

actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);

// 创建一个新的SpinnerAdapter,它包含了下拉列表中显示的值。

ArrayAdapter dropDownAdapter =  ArrayAdapter.createFromResource(this,                                  R.array.my_dropdown_values,                                  android.R.layout.simple_list_item_1);


// 分配回调来处理下拉列表的选择。

actionBar.setListNavigationCallbacks(dropDownAdapter,  new OnNavigationListener() {    public boolean onNavigationItemSelected(int itemPosition,                                            long itemId) {
// 根据选择的下拉列表项的位置修改UI。

return true;  }});


代码片段PA4AD_Ch10_ActionBar/src/ActionBarDropDownActivity.java

当用户从下拉列表中选择一项时,onNavigationItemSelected处理程序就会被触发。根据新选择项使用itemPositionitemId参数确定该如何修改UI

下拉列表选择通常用来细化现有的内容,如在电子邮件客户端中选择一个特殊的账户或者标签。

4. 使用自定义导航View

Tab键和下拉列表都不适合的情况下,操作栏允许使用setCustomView方法添加自己的自定义View(包括布局)

actionBar.setDisplayShowCustomEnabled(true);

actionBar.setCustomView(R.layout.my_custom_navigation);

自定义View会出现在同Tab键或下拉列表一样的位置——在应用程序图标的右侧,但在任意操作的左侧。为了在应用程序间保持一致性,通常最好使用标准的导航模式。

1.1.3 操作栏操作简介

操作栏的右侧用来显示“操作”和相关的溢出菜单,如图1-8所示。

1-8

操作栏操作和溢出菜单是在Android 3.0(API level 11)中连同操作栏本身一起引入的,它们代替了硬件菜单键和相关联的选项菜单。同样,使用同前面用来创建和管理选项菜单相同的API来填充它们。这一过程将在本章的后面做详细的介绍。

操作就是菜单项,是对用户总是可见的、容易使用的重要菜单项。操作栏应该是建立在其他类似应用程序中常见操作的基础上的,是用户经常使用的、对用户非常重要的、最期待的菜单项。一般的、很少用到的选项,如设置、帮助或者关于本应用程序,应该永远不会在操作项中出现。

一般来说,操作项应该是那些不依赖于当前上下文的全局操作。Activity内的导航——例如,改变Tab键或者从导航下拉列表中选择一个选项——不应该改变可用的操作项。

1.2 向地震监控程序添加一个操作栏

在下面的示例地震监控程序中(在第9章中把它的处理移到后台进行),将包含一个操作栏以使其变得更强大。

在第下一章中,将向地震监控程序添加一个地图,所以利用这个机会先向应用程序中添加一些导航元素以便支持地图。

(1) 首先修改清单文件。添加一个uses-sdk节点,指定目标SDKAPI level 15,最小SDK版本为11——它是第一个支持操作栏的平台版本。

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"      package="com.paad.earthquake"      android:versionCode="1"      android:versionName="1.0">     <uses-sdk android:targetSdkVersion="15"              android:minSdkVersion="11" />     [... Existing Manifest nodes ...] </manifest>


(2) 利用这个机会,通过添加一个徽标和操作栏背景来创建自定义的品牌打造。作为练习,你可以任意进行设计。通过更新清单文件中的Earthquake Activity节点,为手机屏幕启用拆分操作栏:

<activity  android:name=".Earthquake"  android:label="@string/app_name"  android:uiOptions="splitActionBarWhenNarrow">  <intent-filter>    <action android:name="android.intent.action.MAIN" />    <category android:name="android.intent.category.LAUNCHER" />  </intent-filter></activity>
(3) 地图将在它自己的 Fragment中显示。首先在 res/layout文件夹下创建一个新的 map_fragment.xml布局。在 13章中将使用一个 Map View代替它,但是现在先用一个 Text View作为占位符。
<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"  android:layout_width="match_parent"  android:layout_height="match_parent">  <TextView    android:id="@+id/textView1"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:text="Map Goes Here!"  /></FrameLayout>
  

(4) 创建Map Fragment。创建一个新的扩展FragmentEarthquakeMapFragment类,并重写onCreateView处理程序来填充map_fragment布局。

import android.app.Fragment;import android.os.Bundle;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;public class EarthquakeMapFragment extends Fragment  {  @Override  public View onCreateView(LayoutInflater inflater, ViewGroup container,      Bundle savedInstanceState) {    View view = inflater.inflate(R.layout.map_fragment, container, false);    return view;  }}
(5) 现在考虑要使用的布局。在手机设备上,在任何给定的时间内,最好只显示列表或者地图。在平板设备上,并排显示的多面板方式将会创建更加迷人的 UI。在 res/layout-sw720dp文件夹下创建一个 main.xml变体。这个文件夹装饰要求设备有720dp的可用屏幕宽度,以便能够正常显示内容。这个新的布局应该并排显示Earthquake List FragmentEarthquake Map Fragment。在这个布局中,限定List Fragment的宽度为这个布局最小宽度的一半(360dp)

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  android:orientation="horizontal"  android:layout_width="match_parent"  android:layout_height="match_parent">  <SearchView    android:id="@+id/searchView"    android:iconifiedByDefault="false"    android:background="#FFF"    android:layout_width="wrap_content"    android:layout_height="wrap_content">  </SearchView>  <fragment    android:name="com.paad.earthquake.EarthquakeListFragment"    android:id="@+id/EarthquakeListFragment"    android:layout_width="360dp"    android:layout_height="match_parent"  />  <fragment android:name="com.paad.earthquake.EarthquakeMapFragment"    android:id="@+id/EarthquakeMapFragment"    android:layout_width="fill_parent"    android:layout_height="match_parent"  /></LinearLayout>


(6) 为了支持以前的平台版本,需要将新的平板布局文件复制到layout-xlarge文件夹下。

(7) 对小屏幕来说,需要使用操作栏Tab键在列表和地图Fragment之间进行切换。首先,修改res/layout文件夹下的main.xml文件。使用一个Frame Layout替换包含有Earthquake List Fragmentfragment节点,这个Frame Layout将作为列表或者地图Fragment的容器:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  android:orientation="vertical"  android:layout_width="match_parent"  android:layout_height="match_parent">  <SearchView    android:id="@+id/searchView"    android:iconifiedByDefault="false"    android:background="#FFF"    android:layout_width="wrap_content"    android:layout_height="wrap_content">  </SearchView>  <FrameLayout    android:id="@+id/EarthquakeFragmentContainer"    android:layout_width="match_parent"    android:layout_height="match_parent">  </FrameLayout></LinearLayout>


(8) 新的布局意味着Earthquake List Fragment将不会一直可用,因此回到Earthquake Activity并修改onActivityResult处理程序,直接使用Service更新地震数据:

@Overridepublic void onActivityResult(int requestCode, int resultCode, Intent data) {  super.onActivityResult(requestCode, resultCode, data);   if (requestCode == SHOW_PREFERENCES) {    updateFromPreferences();    startService(new Intent(this, EarthquakeUpdateService.class));  }}


(9) 现在添加在列表和地图之间切换(一次只可以显示一种Fragment)的导航支持。首先创建一个新的扩展ActionBar.TabListenerTabListener。应该采用一个容器和Fragment类,当Tab键选中时将该Fragment展开到容器中,在未选中Tab键时将FragmentUI中分离出来。

public static class TabListener<T extends Fragment>  implements ActionBar.TabListener {   private Fragment fragment;  private Activity activity;  private Class<T> fragmentClass;  private int fragmentContainer;   public TabListener(Activity activity, int fragmentContainer,                     Class<T> fragmentClass) {     this.activity = activity;    this.fragmentContainer = fragmentContainer;    this.fragmentClass = fragmentClass;  }


// 当一个新Tab键被选中时调用

public void onTabSelected(Tab tab, FragmentTransaction ft) {    if (fragment == null) {      String fragmentName = fragmentClass.getName();      fragment = Fragment.instantiate(activity, fragmentName);      ft.add(fragmentContainer, fragment, fragmentName);    } else      ft.attach(fragment);  }


// 当其他的Tab键被选中时,在当前选中的Tab键上调用

public void onTabUnselected(Tab tab, FragmentTransaction ft) {    if (fragment != null)      ft.detach(fragment);  }   //  当选中的Tab键被再次选中时调用  public void onTabReselected(Tab tab, FragmentTransaction ft) {    if (fragment != null)      ft.attach(fragment);  }}


(10) 还是在Earthquake Activity中,修改onCreate处理程序以检测是否需要Tab键导航;如果需要,启用Tab键模式并为列表和地图Fragment创建新的Tab键,并使用第(8)步中创建的TabListener处理导航操作:

TabListener<EarthquakeListFragment> listTabListener;TabListener<EarthquakeMapFragment> mapTabListener; @Overridepublic void onCreate(Bundle savedInstanceState) {  [... Existing onCreate ...]   ActionBar actionBar = getActionBar();   View fragmentContainer = findViewById(R.id.EarthquakeFragmentContainer); //如果列表和地图Fragment都可用,使用平板电脑导航  boolean tabletLayout = fragmentContainer == null; //如果不是平板电脑,使用操作栏Tab键导航  if (!tabletLayout) {    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);    actionBar.setDisplayShowTitleEnabled(false);     //  创建并添加列表Tab键    Tab listTab = actionBar.newTab();     listTabListener = new TabListener<EarthquakeListFragment>      (this, R.id.EarthquakeFragmentContainer, EarthquakeListFragment.class);     listTab.setText("List")           .setContentDescription("List of earthquakes")           .setTabListener(listTabListener);     actionBar.addTab(listTab);     //  创建并添加地图Tab键    Tab mapTab = actionBar.newTab();     mapTabListener = new TabListener<EarthquakeMapFragment>      (this, R.id.EarthquakeFragmentContainer, EarthquakeMapFragment.class);     mapTab.setText("Map")          .setContentDescription("Map of earthquakes")          .setTabListener(mapTabListener);     actionBar.addTab(mapTab);  }}


(11) 当由于配置改变导致Activity重新启动时,Fragment Manager会尝试还原Activity中显示的Framgent。通过重写onSaveInstanceStateonRestoreInstanceStateonResume处理程序,可以保证操作栏Tab键和它相关联的TabListener与可见的Tab键是同步的。

a. 首先重写onSaveInstanceState处理程序来保存当前选择的操作栏Tab键并从当前视图中将每个Fragment分离出来。

private static String ACTION_BAR_INDEX = "ACTION_BAR_INDEX"; @Overridepublic void onSaveInstanceState(Bundle outState) {  View fragmentContainer = findViewById(R.id.EarthquakeFragmentContainer);  boolean tabletLayout = fragmentContainer == null;   if (!tabletLayout) {    //  保存当前操作栏Tab键的选择    int actionBarIndex = getActionBar().getSelectedTab().getPosition();    SharedPreferences.Editor editor = getPreferences(Activity.MODE_PRIVATE).edit();    editor.putInt(ACTION_BAR_INDEX, actionBarIndex);    editor.apply();     //  分离每个Fragment    FragmentTransaction ft = getFragmentManager().beginTransaction();    if (mapTabListener.fragment != null)      ft.detach(mapTabListener.fragment);    if (listTabListener.fragment != null)      ft.detach(listTabListener.fragment);    ft.commit();  }   super.onSaveInstanceState(outState);}


b. 重写onRestoreInstanceState处理程序以找到任意已经创建的Fragment并将它们分配给关联的Tab Listener

@Overridepublic void onRestoreInstanceState(Bundle savedInstanceState) {  super.onRestoreInstanceState(savedInstanceState);   View fragmentContainer = findViewById(R.id.EarthquakeFragmentContainer);  boolean tabletLayout = fragmentContainer == null;   if (!tabletLayout) {


//获得重建的Fragment并把它们分配给相关的TabListener

listTabListener.fragment =      getFragmentManager().findFragmentByTag(EarthquakeListFragment.class.getName());    mapTabListener.fragment =      getFragmentManager().findFragmentByTag(EarthquakeMapFragment.class.getName());
//还原之前的操作栏Tab键选择
 SharedPreferences sp = getPreferences(Activity.MODE_PRIVATE); int actionBarIndex = sp.getInt(ACTION_BAR_INDEX, 0); getActionBar().setSelectedNavigationItem(actionBarIndex); }}c. 最后,重写onResume处理程序,还原之前选择的操作栏Tab键。@Overridepublic void onResume() { super.onResume(); View fragmentContainer = findViewById(R.id.EarthquakeFragmentContainer); boolean tabletLayout = fragmentContainer == null;  if (!tabletLayout) { SharedPreferences sp = getPreferences(Activity.MODE_PRIVATE); int actionBarIndex = sp.getInt(ACTION_BAR_INDEX, 0); getActionBar().setSelectedNavigationItem(actionBarIndex); }

运行在手机上的应用程序应该显示包含有两个Tab键的操作栏——一个显示地震数据的列表,另一个显示地图。在平板设备上,应该并排显示两个Fragment

《Android 4 高级编程(第3版)》试读电子书免费提供,有需要的留下邮箱,一有空即发送给大家。 别忘啦顶哦!

更多相关文章

  1. Android之广播机制
  2. adb 操作指令详解
  3. 安卓反编译揭秘(爱加密系列教程九)
  4. 如何用Qt/C++访问Android摄像头
  5. 如何安装Android应用程序到sdcard上
  6. Kivy A to Z -- 调试篇之在Android平台调试Python代码
  7. Flutter教程(一) 十分钟了解Flutter
  8. Android(安卓)== 简单的binder通信
  9. Android显示原理

随机推荐

  1. Android--socket 发送广播的那些坑
  2. Android 初始化之Zygote
  3. Android之基于xmpp openfire smack开发之
  4. android drawable-hdpi xhdpi xxhdpi xxx
  5. 华为十年资深架构师推荐最强Android 架构
  6. Android studio 通过以servlet搭建的服务
  7. 转:Debug Native c/c++ Application for A
  8. Android进化史
  9. Android(安卓)DEX自动拆包及动态加载简介
  10. Android 测试代码编写小技巧 - UI 和 单