概述:
Notification是可以显示在APP外的给用户看的信息, 这东西一般显示在屏幕的顶部,当我们告诉Android要显示一个notification的时候, 它首先会以一个小图标的方式出现在notification area. 如果用户想要查看详情, 他可以打开notification drawer. Notification area和notification drawer都是有Android控制的让用户可以使用看到的区域. 这是手机上的notification area:
下面这个是notification drawer:
创建一个Notification:
想要创建一个Notification, 我们需要Notification.Builder对象, 并为其指定UI和动作. 然后使用Notification.Builder.build()方法返回一个Notification对象. 想要使用这个Notification对象的话, 我们则需要调用NotificationManager.notify()方法.
一个Notification对象必须包含下列的部分:
一个小的icon, 通过setSmallIcon()指定.
一个title, 通过setContentTitle()指定.
默认文本, 通过setContentText()指定.
除了上述的3个部分之外, 其它的部分则都是可选的.
Notification的动作(action):
Notification的action(动作)是可选的, 但是我们应该至少为其指定一个action, 它让用户可以通过点击action启动一个Activity, 在APP的内部还可以对该动作进行更多的操作.
一个Notification可以提供多个action, 我们应该总是提供一个点击事件的action, 通常情况下这个action会打开一个我们APP内部的Activity. 我们还可以在Notification上增加按钮, 以实现比如回复一条文字消息这种操作(Android4.1加入).
在Notification内部, action本身是由一个PendingIntent定义的, 其中包含一个Intent, 可以启动一个APP内部的Activity. 为了将PendingIntent与某个手势关联, 我们需要调用NotificationCompat.Builder中的方法. 比如, 如果我们需要在用户点击Notification的时候启动一个Activity,那么需要通过setContentIntent()来添加一个PendingIntent.
当用户点击Notification的时候启动一个Activity可能是最常用的操作了, 我们还可以在用户关闭一个Notification的时候启动一个Activity. 在Android4.1及以上的版本中, 我们可以通过Notification上的按钮来启动一个Activity.
Notification的优先级(priority):
如果有需要的话我们可以为Notification设置优先级, 优先级指定了设备UI应该如何显示Notification. 我们可以调用NotificationCompat.Builder.setPriority()方法并传入一个NotificationCompat优先级常量给它作为参数. 有5个优先级等级, 从PRIORITY_MIN(-2)到PRIORITY_MAX(2),如果没设置的话, 那么默认的优先级是PRIORITY_DEFAULT(0).
创建一个简单的Notification:
下面的代码是一个简单的Notification, 在用户点击Notification的时候会打开一个Activity. 留意代码中的TaskStackBuilder对象, 它为action创建了一个PendingIntent, 这种方法在下文还会有更详细的介绍. 栗子:
NotificationCompat.Builder mBuilder=
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle("My notification")
.setContentText("Hello World!");
// Creates anexplicit intent for an Activity in your app
Intent resultIntent = new Intent(this,ResultActivity.class);
// The stackbuilder object will contain an artificial back stack for the
// startedActivity.
// This ensuresthat navigating backward from the Activity leads out of
// yourapplication to the Home screen.
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
// Adds the backstack for the Intent (but not the Intent itself)
stackBuilder.addParentStack(ResultActivity.class);
// Adds theIntent that starts the Activity to the top of the stack
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// mId allowsyou to update the notification later on.
mNotificationManager.notify(mId, mBuilder.build());
这时我们的用户就已经收到一个提醒了.
为Notification指定一个layout:
如果想要为我们的Notification指定一个自己的布局, 那么首先需要创建一个带有普通view的NotificationCompat.Builder对象. 然后调用Builder.setStyle(),并传入一个扩展的layout对象作为它的参数. 但是这种方法不适用于Android4.1及以前的版本, 下面的代码展示了如何将之前的那段代码扩展为指定的layout:
NotificationCompat.Builder mBuilder= new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle("Event tracker")
.setContentText("Events received")
NotificationCompat.InboxStyle inboxStyle=
new NotificationCompat.InboxStyle();
String[] events= new String[6];
// Sets a titlefor the Inbox in expanded layout
inboxStyle.setBigContentTitle("Event tracker details:");
...
// Moves eventsinto the expanded layout
for (int i=0; i< events.length; i++){
inboxStyle.addLine(events[i]);
}
// Moves theexpanded layout object into the notification object.
mBuilder.setStyle(inBoxStyle);
...
// Issue thenotification here.
关于兼容性的处理:
不是所有的Notification的特性都可以在特定的版本可以得到支持的, 比如action button, 只有在Android4.1及以上版本中才能够得到支持, 因为扩展Notification本身就需要Android4.1以上的版本. 为了获得更好的兼容性, 我们可以使用NotificationCompat和它的子类, 特别是NotificationCompat.Builder. 另外, 当我们实现一个Notification的时候应该遵循这些流程:
1. 向所有的用户提供所有的Notification功能, 不要在意他们的版本. 为了实现这个, 需要确保所有的功能在我们的APP中的某个Activity中是可用的. 比如, 如果我们想要使用addAction()提供一个控制媒体播放和停止播放的操作, 那么首先应该在我们自己的Activity中实现这个操作.
2. 确保所有的用户可以在Activity中使用这个功能, 为了实现这个, 需要为Activity创建一个PendingIntent. 调用setContentIntent()方法将PendingIntent传给Notification.
3. 现在可以添加Notification的扩展功能到你想要使用的Notification中了. 请记住任何功能都应该在点击启动的Activity中可用. 比如我们要扩展一个可以用于聊天的Notification, 那么必须保证点击这个Notification打开的那个Activity中, 也可以实现聊天的功能.
管理Notification:
当我们多次使用同一个Notification的时候, 我们应该考虑修改之前使用过的Notification的值或者对其添加数据, 而不是重新创建一个新的Notification. 比如Gmail通过增加未读消息的数量和增加email的概述到Notification来提醒用户新邮件到了. 这种方法叫做”stacking(堆叠)” Notification(Gmail的这个功能用到了”inbox”扩展layout, 只有在Android4.1以上才支持). 在这里对这个概念有更多的阐述.
下面开始描述如何更新Notification和如何删除它们.
更新Notification:
想要将一个Notification设置为可更新的Notification, 我们需要使用NotificationManager.notify()方法来显示它, 并传入一个NotificationID作为参数. 如果想要在创建Notification之后就更新它, 那么需要创建一个NotificationCompat.Builder, 通过该Builder创建一个Notification,然后使用与之前相同的ID发布这个新的Notification. 如果之前的Notification依然是可见的, 那么Android将会更新该Notification为新的内容. 如果之前的Notification已经消失了, 那么会创建一个新的.
这里是一段更新Notification的代码:
mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Sets an IDfor the notification, so it can be updated
int notifyID =1;
mNotifyBuilder = newNotificationCompat.Builder(this)
.setContentTitle("New Message")
.setContentText("You've received new messages.")
.setSmallIcon(R.drawable.ic_notify_status)
numMessages = 0;
// Start of aloop that processes data and then notifies the user
...
mNotifyBuilder.setContentText(currentText)
.setNumber(++numMessages);
// Because theID remains unchanged, the existing notification is
// updated.
mNotificationManager.notify(
notifyID,
mNotifyBuilder.build());
...
删除Notification:
在下列的任何一个事件发生之前, Notification会一直存在;
1. 用户选择删除Notification或者执行了”clear all”.
2. 用户点击了Notification, 并且我们在创建该Notification的时候调用了setAutoCancel()方法.
3. APP自己调用了cancel()并指定了NotificationID. 这个方法也会删除”ongoing”Notification.
4. APP自己调用了cancelAll()方法, 会删除之前所有APP发布的Notification.
当启动一个Activity的时候保留导航:
当我们从Notification启动一个Activity的时候, 必须考虑到用户预期的导航体验. 点击退后的时候应该回到APP的正常流程, 比如到主界面. 考虑到用户的体验, 我们应该在一个新的Task中启动Activity. 我们如何设置PendingIntent以获得一个新的Task取决于我们要启动的Activity的性质. 这里有两种常用的解决方案:
1. 常规Activity. 就是我们的APP中已经存在的Activity, 平时不通过Notification也可以用到这个Activity. 在这种情况下, 需要设置PendingIntent启动一个新的Task, 并且提供Task后的堆栈, 这样用户在进入了这个Activity之后的后退事件就可以得到处理了.
Gmail的Notification刚好演示了这种功能. 当我们点击一个单一message的Notification的时候, 我们可以看到message本身. 点击后退按钮的时候会让我们退回到桌面, 就好像我们从桌面直接启动Gmail, 而不是从Notification启动它. 这种情况会在点击Notification的时候发生, 点击Notification所处的位置. 比如我们正在Gmail中写一个email, 这时候点击了一个带有新的email的Notification, 我们会直接被导航到这个email, 点击返回键的时候会被导航到邮箱列表, 然后到主界面(Android桌面), 而不是带回到之前写email的地方.
2. 特殊Activity. 用户只能从Notification启动这个Activity. 在某种意义上说这个Activity只是为了显示Notification未能全部显示的信息. 在这种情况下, 需要设置一个PendingIntent来启动一个新的Task. 但是不需要创建一个堆栈(不需要考虑按回退键), 因为启动的Activity不是APP中的Activity的一部分. 点击后退键的时候将会让用户回到Android的主界面.
那么现在开始看一下如何启动上述两种Activity.
设置一个常规Activity的PendingIntent:
设置常规Activity的PendingIntent的步骤是酱婶儿的:
1. 在Manifest中定义一个Activity.
A. 为Android4.0.3和更早的版本提供支持, 需要这样做, 通过<meta-data>指定Activity的父Activity,
B. 为Android4.1及更新的版本提供支持, 需要这样做, 为<activity>指定android:parentActivityName属性.
最后的XML文件长这样:
<activity
android:name=".MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".ResultActivity"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity"/>
</activity>
2. 为Activity指定堆栈, 也就是为了按下返回键时候的反应.
A. 创建一个Intent来启动Activity.
B. 通过TaskStackBuilder.create()方法创建一个堆栈(stack).
C. 通过addParentStack()向stack builder中添加堆栈(stack).
D. 通过addNextIntent()方法为要启动的Activity指定Intent. 传入步骤A中创建的Intent.
E. 如果需要的话可以向Intent对象中添加参数, 需要使用TaskStackBuilder.editIntentAt(), 在有些情况下需要保证按返回键后显示的Activity是有意义的, 所以可能会用到这样的功能.
F. 通过getPendingIntent()方法取得PendingIntent, 然后我们可以使用这个PendingIntent作为setContentIntent()方法的参数.
下面的代码段描述了这一过程:
...
Intent resultIntent = new Intent(this,ResultActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
// Adds the backstack
stackBuilder.addParentStack(ResultActivity.class);
// Adds theIntent to the top of the stack
stackBuilder.addNextIntent(resultIntent);
// Gets aPendingIntent containing the entire back stack
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(0,PendingIntent.FLAG_UPDATE_CURRENT);
...
NotificationCompat.Builder builder= new NotificationCompat.Builder(this);
builder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(id, builder.build());
设置一个特殊Activity的PendingIntent:
特殊Activity不需要一个堆栈, 所以我们不需要调用addParentStack()来创建一个堆栈. 但是需要在manifest中设置Activity的Task选项, 并通过getActivity()来创建PendingIntent.
1. 在manifest中, 为Activity增加以下属性:
android:name=”activityclass:Activity的完整的类名.
android:taskAffinity=””:跟FLAG_ACTIVITY_NEW_TASK标志一起使用可以确保Activity不被分配到系统默认的task中.
android:excludeFromRecents=”true”:从”最近使用”中排出新的task, 这样用户就不能通过导航回到这个activity了.
<activity
android:name=".ResultActivity"
...
android:launchMode="singleTask"
android:taskAffinity=""
android:excludeFromRecents="true">
</activity>
...
2. 创建并发布一个Notification:
A. 创建一个需要启动的Activity的Intent.
B. 通过setFlag()设置FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TASK来设置Activity被创建在一个新的空的task中.
C. 对Intent设置任何其它想要设置的选项.
D. 通过getActivity()创建一个PendingIntent. 我们可以使用这个PendingIntent作为setContentIntent()方法的参数.
下面的代码段描述了这一过程:
// Instantiate a Builder object.
NotificationCompat.Builder builder= new NotificationCompat.Builder(this);
// Creates anIntent for the Activity
Intent notifyIntent =
new Intent(this,ResultActivity.class);
// Sets theActivity to start in a new, empty task
notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
// Creates thePendingIntent
PendingIntent notifyPendingIntent =
PendingIntent.getActivity(
this,
0,
notifyIntent,
PendingIntent.FLAG_UPDATE_CURRENT
);
// Puts thePendingIntent into the notification builder
builder.setContentIntent(notifyPendingIntent);
// Notificationsare issued by sending them to the
//NotificationManager system service.
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Builds ananonymous Notification object from the builder, and
// passes it tothe NotificationManager
mNotificationManager.notify(id, builder.build());
在Notification中显示进度:
Notification中可以包含一个进度条用来显示进度. 如果我们可以确定执行操作需要的时间那么就可以使用一个进度条, 如果不能确定, 那么就得使用一种”不确定”的指示器. 显示进度条需要ProgressBar类, 从Android4.0开始, 显示一个进度条使用setProgress()方法, 对于更早的版本, 我们必须创建自己自定义的Notification layout, 并且包含一个ProgressBar的控件.
显示进度条:
要显示这样的一个进度条, 就得先调用setProgress(max, progress, false)方法来添加一个bar到Notification上面, 然后再发布这个Notification.随着进度的增加, 增加参数中的progress, 然后更新Notification. 在进度条走到终点的时候, progress参数应该跟max相等. 一个常用的方法是将max设置为100, 然后按照百分比来调整progress.
Progress bar在进度走完或者我们主动移除它的情况下会消失,不管是主动的还是已经走完了, 我们都应该记得在结束之后修改Notification的说明文字, 想要移除Notification的话, 可以使用setProgress(0, 0, false)方法. 栗子:
...
mNotifyManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mBuilder = newNotificationCompat.Builder(this);
mBuilder.setContentTitle("Picture Download")
.setContentText("Download in progress")
.setSmallIcon(R.drawable.ic_notification);
// Start alengthy operation in a background thread
new Thread(
new Runnable(){
@Override
public void run(){
int incr;
// Do the "lengthy" operation 20times
for (incr= 0; incr<= 100; incr+=5){
// Sets the progress indicator to a maxvalue, the
// current completion percentage, and"determinate"
// state
mBuilder.setProgress(100, incr,false);
// Displays the progress bar for thefirst time.
mNotifyManager.notify(0, mBuilder.build());
// Sleeps thethread, simulating an operation
// that takestime
try {
// Sleep for 5 seconds
Thread.sleep(5*1000);
} catch (InterruptedException e){
Log.d(TAG,"sleep failure");
}
}
// When the loop is finished, updates thenotification
mBuilder.setContentText("Download complete")
// Removes the progress bar
.setProgress(0,0,false);
mNotifyManager.notify(ID, mBuilder.build());
}
}
// Starts thethread by calling the run() method in its Runnable
).start();
显示一个持续活动的指示器:
要显示一个不确定(进度)的指示器, 我们需要调用setProgress(0, 0, true)(前两个参数是忽略的)方法来将其添加到Notification然后发布这个Notification.结果是跟progress bar一样风格的指示器, 但是它是持续不停的. 一旦操作开始, 动画将会在我们修改Notification之前一直持续直到我们调用setProgress(0, 0, false)并且更新Notification移除它为止. 完成之后还要记得修改Notification的文字描述. 栗子:
将上一段代码中的这个部分:
// Sets the progress indicator to a max value, thecurrent completion
// percentage,and "determinate" state
mBuilder.setProgress(100, incr,false);
// Issues thenotification
mNotifyManager.notify(0, mBuilder.build());
修改为:
// Sets anactivity indicator for an operation of indeterminate length
mBuilder.setProgress(0,0, true);
// Issues thenotification
mNotifyManager.notify(0, mBuilder.build());
参考: http://developer.android.com/guide/topics/ui/notifiers/notifications.html