13.4.3 状态栏通知
状态栏通知(Status Bar Notifications)的使用相比弹出式通知Toast而言要复杂得多。首先,状态栏通知的交互界面样式更为复杂,具有更为丰富的图标和文字内容;其次,状态栏通知需要绑定与之相关的事件,使用户可以与通知项进行交互,对通知所提示的内容进行进一步的处理;再次,状态栏通知的生命周期更为复杂,它不像Toast通知那样定时自动消失,而是有多种多样的控制模式,开发时需要根据场景的不同选择不同的生命周期模式,并维护状态信息的更新和交互事件的修改。
状态栏通知的交互界面,由两部分构成,一部分是状态栏界面,包括在状态栏上显示的图标和文字内容;另一部分是列表栏界面,指的是下拉通知列表中的通知项界面。使用状态栏通知android.app.Notification对象,可以设置这两部分交互界面:
//初始化状态栏上会显示的图标和文字
Notification notification=new Notification(
R.drawable.status_bar_notification,
getString(R.string.status_bar_notification),
System.currentTimeMillis());
//设置下拉通知栏会显示的文字信息
notification.setLatestEventInfo(context,
getString(R.string.notification_title),
getString(R.string.notification_content),
createPendingIntent());
…
随着Android版本的不断升级,状态栏通知的样式也变得更为复杂,于是在Android 3.0中,提供了状态栏通知的构造类android.app.Notification.Builder,通过Notification.Builder可以更清晰地构造状态栏通知对象:
//用新的Builder类来构建同样样式的状态栏通知
Notification notification=new Notification.Builder(context)
.setTicker(getString(R.string.status_bar_notification))
.setSamllIcon(R.drawable.status_bar_notification)
.setContentTitle(getString(R.string.notification_title))
.setContentText(getString(R.string.notification_content)
.setContentIntent(createPendingIntent())
.getNotifcation();
…
除此之外,状态栏通知也支持自定义样式。状态栏通知的构造者和展示者隶属于不同的应用(这一点与Toast通知有很大区别,Toast通知的显示是在构造应用的主线程中完成的),因此,与App Widght的界面构造类似,需要使用RemoteViews来定制交互界面,实现构造和展示的分离:
//使用RemoteViews构造自定义的状态栏通知的列表栏样式
//通知列表栏样式为左边文字,右边图标
RemoteViews contentView=new RemoteViews(getPackageName(),R.layout.my_
notification);
contentView.setImageViewResource(R.id.icon, R.drawable.my_notification_icon);
contentView.setTextViewText(R.id.text, getString(R.string.my_notification);
notification.contentView=contentView;
…
在实际开发中,除非有特别的界面样式需求,尽量不要自定义状态栏通知的样式。因为不同的Android ROM可能会定制不一样的通知栏样式,自定义状态栏通知很可能在一些设备上显示效果不佳,并且与其他通知项格格不入。如果默认的通知栏样式实在无法满足需求,必须使用自定义状态栏通知样式,那么最好使用系统定义的背景和前景样式,保证显示效果与系统风格一致:
自定义的my_notification样式
<!—背景使用系统定义的样式—>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:background="@android:drawable/status_bar_item_background"
…>
<!—左边的文本使用系统定义的样式—>
<TextView android:id="@+id/text"
android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
…/>
<ImageView android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
…/>
</LinearLayout>
在Android中,开发者除了可以为状态栏通知定制提示文本和图标内容,还可以为状态栏通知设置闪烁(Flashing LED)、震动(Vibrate)和提示音(Alert Sound)等效果,进一步增强状态栏通知的提醒效果:
//开启和设置闪烁效果
//开发者可以设定闪烁灯的颜色以及闪烁灯开闭的时长
notification.defaults|=Notification.DEFAULT_LIGHTS;
notification.ledARGB=0xffff0000;//红光闪烁
notification.ledOnMS=400;//开启400毫秒
notification.ledOffMS=800;//然后关闭800毫秒
notification.flags|=Notification.FLAG_SHOW_LIGHTS;
//开启和设置震动效果
//开发者可以控制震动的节奏和时长
notification.defaults|=Notification.DEFAULT_VIBRATE;
//设置震动效果,每两个值为一对,第一个表示开启时间(毫秒),另一个表示持续时长,此例中,一
共会震动两次,在0毫秒震动200毫秒,在300毫秒时再继续震动500毫秒
long[]vibrate={0,200,300,500};
notification.vibrate=vibrate;
//开启和设置提示音的效果
//开发者可以通过Uri或者文件地址设置提示音
notification.defaults|=Notification.DEFAULT_SOUND;
notification.sound=Uri.parse(
"file:///sdcard/my_ring.mp3");//将my_ring设为提示音
与Toast通知不同,状态栏通知不仅具有交互界面,还可以为其绑定相应的交互事件。当用户在通知的下拉列表中点击相应的通知项,就会触发通知所绑定的事件,向目标组件(包括界面组件、服务组件和触发器组件)发送Intent请求。
同样,状态栏通知的事件也是通过PendingIntent来构造、并绑定到通知项中异步触发的:
//createPendingIntent函数,构造通知所需的PendingIntent对象
//构造一个打开邮件组件的新邮件通知
Intent intent=new Intent();
intent.setAction(NEW_EMAIL_ACTION);
intent.setExtra(NEW_EMAIL_ID, newEmailId);
PendingIntent pendingIntent=PendingIntent.getActivity(
context,
0,
intent,
0);
发送状态栏通知,需要使用android.app.NotifcationManager对象。NotificationManager是一个系统服务的客户端对象,它会将请求转发到系统服务NotificationManagerService进行处理。使用NotificationManager.notify可以发送请求:
//获得NotificationManager对象
NotificationManager manager=(NotificationManager)getSystemService(Context.
NOTIFICATION_SERVICE);
//发送notification
manager.notify(MY_ID, notification);
其中,MY_ID是此次状态栏通知的标识[1],通知管理服务会根据这个标识,对所有通知项进行过滤,保证同样标识的通知项只有一个,开发者需要为每个通知项添加全局唯一的标识,例如,使用包名和一些描述信息作为通知项标识:
My.Package.Name.new_mail_notification
通过这样唯一性的标识信息,开发者可以更新或取消通知,比如:
//取消通知
manager.cancel(MY_ID);
在实际开发中,系统有时候无法准确获知用户是否看到了通知,是否该取消通知。为了对状态栏通知的生命周期管理更为智能,Android为Notification添加了一些标志位,使其生命周期更可控。
比如,开发者可以使用Notifcation.FLAG_AUTO_CANCEL标志位,将通知设定为自动取消。一旦用户点击了该通知,通知项会自动消失,无需调用NotificationManager.cancel函数来取消:
//设置为自动取消
notification.flags|=Notification.FLAG_AUTO_CANCEL
再比如,如果状态栏通知非常重要(比如来电通知),需要始终在状态栏保持一个稳定的入口,就可以为通知添加Notification.FLAG_ONGOING_EVENT标志位。这样,在通知列表中就无法通过清除操作取消该通知了(如果没有On-Going的标志位,用户可以在通知列表中清理该通知项),直至程序主动调用NotificationManager.cancel:
//设置为On-Going
notification.flags|=Notification.FLAG_ONGOING_EVENT
注意 本节介绍了Android的全局通知机制,包括通知机制的实现架构、使用方式以及界面构建、事件绑定等内容。
在实际开发中,全局通知是应用与用户进行沟通的最重要方式之一。使用Toast通知,可以实现非阻塞的反馈效果;而使用状态栏通知,则可以实现后台的事件提醒。对于每一个开发者而言,都需要对其有所了解。
[1]在Android 2.0以上的系统里,新增了一个字符串Tag作为辅助标识,默认实现下是null,开发者可以使用Tag和Id共同来标识一次通知。