Notifications
某些情况下需要通知用户你的应用程序中发生了一个事件。一些事件请求用户应答而另外一些则不需要。比如:
- 当一个事件比如保存文件结束时,应该出现一条消息确认保存成功。
- 如果一个后台运行的应用程序需要用户关注,这个应用程序应该创建一个通知来允许用户在方便时进行应答。
- 如果这个应用程序在执行一个用户必须等待的任务(比如加载一个文件),那么应用程序应该显示一个盘旋的进度轮或进度条。
所有这些通知任务可以通过一个不同的技术获取到:
- 一个消息条通知Toast Notification, 用于从后台出现的简短信息。
- 一个状态条通知 Status Bar Notification, 用于来自后台的持续提醒并请求用户应答。
- 一个对话框通知 Dialog Notification, 用于活动相关的通知。
Toast Notification
toast通知是一种在窗口表面弹出的消息。它只占用信息显示所需的空间,用户当前的activity仍保持可见并可交互。该通知自动实现淡入淡出,且不接受人机交互事件。
以下截图展示了闹钟程序的toast通知示例。一旦闹钟被打开,就会显示一条toast作为对设置的确认。
toast能被Activity 或Service创建并显示。如果由Service创建,则toast会显示在当前已获得焦点的Activity前面。
如果需要用户对通知进行响应,可以考虑使用Status Bar Notification。
The Basics
首先,用某个makeText()方法来实例化一个Toast对象。该方法有三个参数:应用程序上下文Context、文本信息和toast的持续显示时间。它将返回一个已正确初始化的Toast对象。可以用show()方法来显示该toast通知,示例如下:
Context context = getApplicationContext(); CharSequence text = "Hello toast!"; int duration = Toast.LENGTH_SHORT; Toast toast = Toast.makeText(context, text, duration); toast.show();
上例演示了大部分toast通知需要的所有内容,应该不大会需要用到其他内容了。不过,你也许想在其他位置显示toast或是要用自己的布局替换默认相对简单的文本消息,下一节将描述如何完成。
还可以将多个方法链接起来写,以避免持久化Toast对象,就像这样:
Toast.makeText(context, text, duration).show();
Positioning your Toast
标准的toast通知左右居中地显示在屏幕底部附近。可以通过setGravity(int, int, int)方法来改变显示位置。它接受三个参数:重力常量常数Gravity,X方向偏移和Y方向偏移。
例如,如果决定把toast置于左上角,可以这样设置重力常数:
toast.setGravity(Gravity.TOP|Gravity.LEFT, 0, 0);
如果想让位置向右移,就增加第二个参数的值;要向下移,就增加最后一个参数的值。
Creating a Custom Toast View
如果不满足于简单的文本消息,还可以为toast通知创建一个自定义布局。要创建自定义布局,需要用XML或程序代码定义一个View布局,然后把根View对象传给setView(View)方法。
例如,可以用以下的XML(保存为toast_layout.xml)创建出下图中所示的布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/toast_layout_root" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent" android:padding="10dp" android:background="#DAAA" > <ImageView android:id="@+id/image" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_marginRight="10dp" /> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="fill_parent" android:textColor="#FFF" /> </LinearLayout>
注意,LinearLayout元素的ID是“toast_layout”。必须用这个ID从XML中解析出布局,如下:
LayoutInflater inflater = getLayoutInflater(); View layout = inflater.inflate(R.layout.toast_layout, (ViewGroup) findViewById(R.id.toast_layout_root)); ImageView image = (ImageView) layout.findViewById(R.id.image); image.setImageResource(R.drawable.android); TextView text = (TextView) layout.findViewById(R.id.text); text.setText("Hello! This is a custom toast!"); Toast toast = new Toast(getApplicationContext()); toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0); toast.setDuration(Toast.LENGTH_LONG); toast.setView(layout); toast.show();
首先,用getLayoutInflater()(或getSystemService())来读取LayoutInflater,然后用inflate(int, ViewGroup)将布局(layout)从XML中解析出来。第一个参数是layout资源ID,第二个参数是根View。可以用解析出来的layout获取其他View对象,之后获取并定义ImageView和TextView元素的内容。最后,用Toast(Context)创建一个新的toast,设置一些属性如gravity和duration等。然后调用setView(View)并将解析出的layout传入。现在就可以调用show()来显示自定义布局的toast了。
注意:除非想用setView(View)来定义布局,否则不要用公共构造方法来构造Toast。如果没有可用的自定义布局,则必须使用makeText(Context, int, int)来创建Toast。
Status Bar Notification
状态栏(status bar)通知将一个图标填加到系统的状态栏中(包含一条可选的提示文本信息),并将一条展开信息添加到通知窗口中。当用户选中展开信息时,Android将执行一个此通知已定义的意图Intent(通常用于弹出一个Activity)。你还可以对通知进行配置,用设备提供的声音、振动、闪光来提醒用户。
当后台服务(Service)需要对某个事件发出提醒并且需要用户响应时,状态栏通知就能发挥作用了。后台服务从来不会启动Activity来接收用户的交互,取而代之的是应该创建一个状态栏通知,在用户点选后再由通知来启动Activity。
以下截图展示了一个左侧带有通知图标的状态栏:
下图展示了“Notifications”窗口内的通知展开信息。用户可通过下拉状态栏(或在Home菜单里选中通知)来显示这个通知窗口。
The Basics
Activity或者Service都能初始化一个状态栏通知。可因为Activity只有在活动状态并获得焦点时才能执行操作,所以还是建议用Service来创建状态栏通知。这样,即使用户正在使用其他程序或者设备已经休眠时,仍然可以从后台创建通知。要创建一个通知,须用到两个类:Notification类和NotificationManager类。
用Notification类的一个实例来定义状态栏通知的属性,比如图标、展开信息,以及播放声音等附属设置。NotificationManager是一个Android系统服务,用于管理和运行所有通知。NotificationManager不能被实例化,为了把Notification传给它,你可以用getSystemService()方法获取一个NotificationManager的引用。在需要通知用户时再调用notify()方法将Notification对象传给它。
要创建一个状态栏通知:
- 获取NotificationManager的引用:
String ns = Context.NOTIFICATION_SERVICE; NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
- 实例化Notification:
int icon = R.drawable.notification_icon; CharSequence tickerText = "Hello"; long when = System.currentTimeMillis(); Notification notification = new Notification(icon, tickerText, when);
- 指定通知的展开信息和Intent:
Context context = getApplicationContext(); CharSequence contentTitle = "My notification"; CharSequence contentText = "Hello World!"; Intent notificationIntent = new Intent(this, MyClass.class); PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
- 将Notification对象传给NotificationManager:
private static final int HELLO_ID = 1; mNotificationManager.notify(HELLO_ID, notification);
好了,现在用户已经能看到通知了。
Managing your Notifications
系统服务NotificationManager管理着所有的通知,只能通过getSystemService()方法来获取它的引用。例如:
String ns = Context.NOTIFICATION_SERVICE; NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
如果想要发送状态栏通知,通过notify(int, Notification)传递Notification对象给NotificationManager即可。第一个参数是Notification 的唯一ID,第二个参数是Notification对象。ID在整个应用程序范围内唯一标识Notification。Notification需要更新或者应用程序可能管理多种不同的通知时需要ID。在用户通过各自定义的Intent返回应用程序时必须能选择正确的动作执行之,因此上述参数是必需的。
要实现用户从通知窗口内点选后自动清除状态栏通知,请在Notification对象中加入“FLAG_AUTO_CANCEL”标志。也可以传入通知ID用cancel(int)手动清除,或者用cancelAll()清除所有你创建的通知。
Creating a Notification
Notification对象定义了通知消息显示在状态栏和通知窗口上的细节内容,以及其他提醒设置(比如:声音、闪光等)。
状态栏通知必须包括以下内容:
- 状态栏图标
- 展开窗口view的标题和展开信息(除非用了自定义展开view)
- PendingIntent,当通知被点选时执行
状态栏通知的可选设置包括:
- 状态栏提示信息
- 提醒声音
- 震动设置
- LED灯闪光设置
Notification的基础库里包含了构造方法Notification(int, CharSequence, long)和setLatestEventInfo(Context, CharSequence, CharSequence, PendingIntent)方法。这已经可以定义Notification的所有设置。以下代码段演示了对通知基本的设置:
int icon = R.drawable.notification_icon; // icon from resources CharSequence tickerText = "Hello"; // ticker-text long when = System.currentTimeMillis(); // notification time Context context = getApplicationContext(); // application Context CharSequence contentTitle = "My notification"; // expanded message title CharSequence contentText = "Hello World!"; // expanded message text Intent notificationIntent = new Intent(this, MyClass.class); PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); // the next two lines initialize the Notification, using the configurations above Notification notification = new Notification(icon, tickerText, when); notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
Updating the notification
应用程序可以在事件正在进行时更新状态栏通知。比如,前一条短信还未读,可又来了一条新短信,短信程序为了正确显示未读短信的总数,可以更新已有的通知。此时,更新原有通知要比向NotificationManager新增一条通知更合理些,因为避免了通知窗口的显示混乱。
因为NotificationManager对每个通知都用一个整数ID进行了唯一标识,新的通知内容可以用setLatestEventInfo()方法方便地进行修改,然后再次调用notify()显示出来。
除了Context、展开信息的标题和文本外,可以利用对象的成员值修改每个属性。要修改通知的文本信息,只能对contentTitle和contentText参数赋新值并调用setLatestEventInfo(),然后再调用notify()方法来更新通知。(当然,如果已经创建了自定义扩展view,那么标题和文本的修改就无效了)。
Adding a sound
可以用缺省提示音(由用户指定)或者程序指定声音来提醒用户。
要使用用户缺省提示音,给defaults属性添加“DEFAULT_SOUND”:
notification.defaults |= Notification.DEFAULT_SOUND;
要使用应用程序指定的声音,则传递一个Uri引用给sound属性。以下例子使用已保存在设备SD卡上的音频文件作为提示音:
notification.sound = Uri.parse("file:///sdcard/notification/ringer.mp3");
在下面的例子里,音频文件从内部MediaStore类的ContentProvider中获取:
notification.sound = Uri.withAppendedPath(Audio.Media.INTERNAL_CONTENT_URI, "6");
这时,已知有资源ID为6的媒体文件,并且已添加到Uri内容中。如果不知道确切的ID,则必须先用ContentResolver查询MediaStore中所有可用的资源。关于使用ContentResolver的详细信息请参阅Content Providers文档。
如果期望在用户响应通知或取消通知前,声音一直持续循环播放,可以把 “FLAG_INSISTENT” 加入flags属性中。
注意:如果defaults属性包含了“DEFAULT_SOUND”,则缺省提示音将覆盖sound 属性里指定的声音。
Adding vibration
可以用缺省震动模式或程序指定的振动模式来提醒用户。
要用缺省震动模式,给属性defaults 添加“DEFAULT_VIBRATE” 即可:
notification.defaults |= Notification.DEFAULT_VIBRATE;
要自定义震动模式,须给vibrate属性传递一个long 类型的数组:
long[] vibrate = {0,100,200,300}; notification.vibrate = vibrate;
长整型数组定义了震动开和关交替的时间(毫秒)。第一个数是开始振动前的等待时间(震动关闭),第二个数是第一次开启振动的持续时间,第三个数是下一次关闭时间,如此类推。振动模式的持续时间没有限制,但不能设置为重复振动。
注意:如果defaults 属性包含了“DEFAULT_VIBRATE”,则缺省的震动模式将会覆盖vibrate 属性里指定的模式。
Adding flashing lights
要想用LED闪光来提醒用户,可以执行缺省闪光模式(如果可用的话),也可以自定义闪光的颜色和模式。
要使用缺省的闪光设置,给属性defaults 添加“DEFAULT_LIGHTS”即可:
notification.defaults |= Notification.DEFAULT_LIGHTS;
要自定义颜色和模式,则须指定ledARGB属性(指颜色)、ledOffMS属性(闪光关闭毫秒数)、ledOnMS属性(闪光开启毫秒数),并在“flags”属性里加入“FLAG_SHOW_LIGHTS”:
notification.ledARGB = 0xff00ff00; notification.ledOnMS = 300; notification.ledOffMS = 1000; notification.flags |= Notification.FLAG_SHOW_LIGHTS;
上例实现了绿色光闪烁300毫秒间歇1秒的闪光。每个设备的LED灯不可能支持所有颜色的发光,不同的设备所能支持的颜色也各不相同,因此硬件将按照最接近的颜色来发光。绿色是最常见的提醒色。
More features
利用Notification的属性和标志位,可以给通知添加更多的特性。
下面列出了其中一些常用的特性:
- “FLAG_AUTO_CANCEL”标志
在flags属性中增加此标志,则在通知窗口点选后能自动取消通知。 - “FLAG_INSISTENT”标志
在flags属性中增加此标志,则在用户响应前一直循环播放声音。 - “FLAG_ONGOING_EVENT”标志
在flags属性中增加此标志,则将通知放入通知窗口的“正在运行”(Ongoing)组中。表示应用程序正在运行——进程仍在后台运行,即使应用程序不可见(比如播放音乐或接听电话)。 - “FLAG_NO_CLEAR”标志
在flags属性中增加此标志,表示通知不允许被“清除通知”按钮清除。这在期望通知保持运行时特别有用。 - number属性
表示通知所代表的事件数量。此数字显示在状态栏图标上。要利用此属性,必须在第一次创建通知时设为1。(如果只是在更新通知时才把此值从0改成任意大于0的数,则数字不会显示出来。) - iconLevel属性
表示通知图标当前的LevelListDrawable等级。通过改变这个值,可以在状态栏中显示图标的动画,这个值与LevelListDrawable中drawable的定义相关。详情请参阅LevelListDrawable。
程序能自定义更多特性,详情请参阅Notification。
Creating a Custom Notification Layout
默认情况下,通知窗口中的展开视图(view)包括基本的标题和文本信息。这是由setLatestEventInfo() 的contentTitle和contentText参数指定的。不过仍可以用RemoteViews来自定义一个展开视图的布局。
下面的截图就展示了一个自定义展开视图的例子,其中用到了LinearLayout 布局中的ImageView和TextView。
要自定义展开信息的布局,需要实例化一个RemoteViews对象,并将它传递给Notification的contentView属性,同时把PendingIntent传给contentIntent属性。
通过例子是对创建自定义展开视图最好的理解方式:
- 为展开视图新建XML布局,建立一个名为custom_notification_layout.xml的布局文件,内容如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent" android:padding="3dp" > <ImageView android:id="@+id/image" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_marginRight="10dp" /> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="fill_parent" android:textColor="#000" /> </LinearLayout>
此布局用于展开视图,但ImageView和TextView的内容还需要由应用程序来定义。RemoteViews提供了一些方便的方法来定义这些内容。 - 在应用程序代码里,用RemoveViews的方法来定义图片和文字。然后把RemoteViews对象传给Notification的contentView属性,如下所示:
RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.custom_notification_layout); contentView.setImageViewResource(R.id.image, R.drawable.notification_image); contentView.setTextViewText(R.id.text, "Hello, this message is in a custom expanded view"); notification.contentView = contentView;
如上:先把程序package名和布局资源ID传给RemoteViews的构造方法。然后用setImageViewResource()和setTextViewText()定义ImageView和TextView的内容,分别把View对象的资源ID、所赋的内容作为参数传入。最后,把RemoteViews对象传给Notification的contentView属性。 - 由于自定义视图不需要执行setLatestEventInfo()方法,因此必须用contentIntent属性来定义一个通知所要执行的意图Intent ,如下:
Intent notificationIntent = new Intent(this, MyClass.class); PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); notification.contentIntent = contentIntent;
-
现在通知可以如常发送了:
mNotificationManager.notify(CUSTOM_VIEW_ID, notification);
RemoteViews类还包含了一些方法,便于在通知的展开视图里增加Chronometer或ProgressBar。关于用RemoteViews创建自定义布局的详细信息,请参阅RemoteViews 类的参考文档。
注意:当建立一个自定义展开视图时,必须特别小心,保证自定义的布局能正常工作在不同的设备方向和分辨率下。这个建议对于所有在Android上创建的View布局都是适用的,但在这种情况下尤为重要,因为布局实际可用的屏幕区域非常有限。不要把自定义布局设计得过于复杂,并且一定要在各种环境配置下进行测试。
Dialog Notification
一个对话框通常是出现在当前活动前面的一个小窗口。背后的活动丢失焦点而由这个对话框接受所有的用户交互。对话框通常用做和运行中应用程序直接相关的通知和短暂活动。
你应该使用对话框来显示一个进度条或者一个需要用户确认的短消息(比如带有“确认”和“取消”按钮的一个警告)。你也可以把对话框作为构成应用程序界面整体的组件以及用于除了通知之外的其它目的。