通知
通知是能在应用的普通用户界面外显示给用户的一种消息。
当你告诉系统公布一条通知时,它首先在通知栏中表现为一枚图标。
用户打开通知抽屉后就能查看通知的细节了。
通知栏和通知抽屉都是由系统控制的区域,用户能够不论什么时间都能查看他们。
图1. 在通知栏里的通知。
图2. 在通知抽屉里的通知。
通知的设计
通知,作为Android用户界面里的很重要的一部分有着它自己的设计指南。阅读Android设计指南 Notifications 学习怎样设计通知和它们的交互。
注解:除非特别指明,指南里引用的是版本为4的支持库中的 NotificationCompat.Builder
类。Notification.Builder
类直到Android 3.0才被引入。
通知的显示元素
依据通知抽屉的版本号和的状态,在通知抽屉里的通知会有两种视觉样式:
- 普通视图
- 在通知抽屉里的通知的标准视图。
- 大视图
- 当通知展开时可见的大视图。大视图是展开的通知的一部分,Android 4.1以上可用。
以下的章节会详细介绍这些样式。
普通视图
普通视图的通知显示在一个高度不超过64dp的区域里。即使你创建了一个大视图样式的通知,在展开前它依然以普通视图显示。以下是普通视图的事例:
图3. 普通视图的通知。
图例中的编号请參考以下的说明:
- 内容标题
- 大号图标
- 内容文本
- 内容信息
- 小号图标
- 通知出现的时间。
你能够使用
setWhen()
明白指定数值。假设没指定的话,默认时间是系统接受到通知的时间。
大视图
通知的大视图仅当通知展开时才显示,也就是当通知在通知抽屉的顶部或用户用手势展开通知时。展开通知功能从Android 4.1開始可用。
以下的截图展示的是一个收件箱样式的通知:
图4. 大视图通知。
注意大视图和普通视图的大部分视觉元素都同样。唯一的不同是插图7的详情区域。
每种大视图样式以不同的点缀。可用的样式为:
- 大图样式
- 这个细节区域包括了一张最大256dp高度的位图。
- 大文本样式
- 在细节区域显示一个大号文本块。
- 收件箱样式
- 在细节区域显示文本行。
全部的大视图样式相同拥有以下的内容选项,这些选项不会在普通视图中生效:
- 大内容标题
- 同意你重写普通视图的内容标题。该标题仅在展开的视图中显示。
- 摘要文本
- 同意你在细节区域下加入一行文本。
将大视图样式应用到通知中在 Applying a big view style to a notification 章节中有具体描写叙述。
创建通知
你能够使用 NotificationCompat.Builder 对象指定通知的用户界面信息和操作。调用 NotificationCompat.Builder.build() 创建通知会返回一个包括你要求的 Notification 对象。
通过调用 NotificationManager.notify() 来把 Notification
对象传递给系统就能公布一个通知了。
必要的通知内容
Notification
对象必须包括下列内容:
- 小号图标,通过
setSmallIcon()
设置 - 标题,通过
setContentTitle()
设置 - 具体文本,通过
setContentText()
设置
可选的通知内容和设置
除必要的通知内容之外其它的通知内容和设置都是可选的。
了解很多其它内容,请阅读 NotificationCompat.Builder 的參考文档。
通知操作
虽然他们是可选的。你应该至少为你的通知加入一个操作。操作能够使用户从通知直接进入你应用中的 Activity ,在那里他们能直观的看到一个或很多其它的事件或做更进一步的事。
通知也能够提供多个操作。你应该总是定义用户点击通知时触发的操作,通常这样的操作会打开你应用中的一个 Activity
。
你也能够把button加入到通知里来处理比方暂停闹钟或马上回复文本信息的附加操作,该功能从Android 4.1起可用。假设使用附加操作button,你相同必须使他们在在应用中的 Activity 里功能可用,阅读 Handling compatibility 章节获取很多其它具体信息。
在通知里,操作是由一个 PendingIntent 对象定义的,该对象包括了一个启动应用里某个 Activity 的 Intent
对象。调用 NotificationCompat.Builder 里的合适的方法就能把手势和 PendingIntent 关联起来。比如,假设你想在用户点击通知抽屉里的通知文本时打开 Activity。你能够调用 setContentIntent() 加入 PendingIntent 来实现。
当用户点击通知时打开 Activity 是最经常使用的操作方案。
你还能够在用户忽略通知时打开 Activity
。
在Android 4.1和以上版本号中还能够从操作button中打开 Activity。
了解很多其它内容。请阅读 NotificationCompat.Builder
的參考指南。
创建简单的通知
以下的代码举例创建了一个简单的通知,当用户点击通知时会打开activity。注意这段代码创建了一个 TaskStackBuilder
对象然后用它为操作创建 PendingIntent
。这样的模式在 Preserving Navigation when Starting an Activity 章节中有更具体的解释。
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle("My notification")
.setContentText("Hello World!");
// 为你应用中的activity创建一个显式的intent
Intent resultIntent = new Intent(this, ResultActivity.class);
// 堆栈创建器对象会为将要启动的Activity创建一个导航回退栈。
// 这样能够确保在Activity后退导航时能够直接把应用回退到主屏幕上。
TaskStackBuilder stackBuilder
=
TaskStackBuilder
.create
(
this
);
// 为这个Intent加入回退栈 stackBuilder
.addParentStack
(
ResultActivity
.
class
);
// 把开启Activity的Intent加入到栈顶 stackBuilder
.addNextIntent
(resultIntent
);
PendingIntent resultPendingIntent
= stackBuilder
.getPendingIntent
(
0
,
PendingIntent
.FLAG_UPDATE_CURRENT
); mBuilder
.setContentIntent
(resultPendingIntent
);
NotificationManager mNotificationManager
=
(
NotificationManager
) getSystemService
(
Context
.NOTIFICATION_SERVICE
);
// mId能够使你以后再更新通知。 mNotificationManager
.notify
(mId
, mBuilder
.build
());
就是这样。你的用户如今被通知到了。
为通知应用大视图样式
想要使通知在展开时以大视图样式呈现,首先使用你想要的普通视图选项去创建一个 NotificationCompat.Builder
对象,接着用大视图样式对象作为參数调用 Builder.setStyle()
。
切记展开的通知在Android 4.1之前的平台上无法使用。
阅读 Handling compatibility 章节,学习怎样在Android 4.1和更早的平台上处理通知。
比如,以下的代码片段演示了怎样使用收件箱的大视图样式改动前面代码片段创建的通知:
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];
// 为收件箱样式的大视图设置标题
inboxStyle.setBigContentTitle("Event tracker details:");
...
// 填充大视图
for (int i=0; i < events.length; i++) {
inboxStyle.addLine(events[i]);
}
// 将大视图样式对象传递给通知对象。
mBuilder.setStyle(inBoxStyle);
...
// 公布通知
处理兼容性
虽然使用了支持库里 NotificationCompat.Builder 的方法设置了通知,可是对于某些特别的版本号来说,并非全部的通知特征都能生效。比如。操作button依赖于展开的通知。由于展开的通知仅在Android 4.1或更高的平台上可用。所以它也仅仅能在Android 4.1或更高的平台上出现。
为了得到最好的兼容性,请使用 NotificationCompat
和它的子类来创建通知。 尤其是 NotificationCompat.Builder
。
除此之外,在创建通知时请遵守以下的步骤:
- 不管用户正在使用的是什么版本号的平台,为他们提供所有通知的功能。要达到这样的效果。须要在你应用中的一个 Activity 里验证所有功能是否可用。
你可能须要加入一个新的 Activity 来验证。
比如,假设你想使用 addAction() 来加入一个能够开关媒体回放功能的控制器,首先你应该在你应用中的一个 Activity 里实现这个控制器。
- 确保全部用户能够在这个
Activity
里通过点击通知能够接触到这个功能。要达到这样的效果。为这个 Activity 创建一个PendingIntent
,通过调用setContentIntent()
来把这个PendingIntent
加入到通知中。 - 如今把你想要使用的扩展通知特征加入到通知中。
切记当用户点击通知时触发启动的全部你加入的功能必须是在 Activity 里可用。
管理通知
当你须要为相同类型的事件多次公布通知时,你应该避免创建一个全新的通知。相反。你应该考虑更新先前的通知,你能够改变它的一些值或加入新的值。
比如,Gmail通过新增未读消息数目和把每一个邮件的概要加入到通知来通知用户已接受到新的邮件。
这杯称为“堆叠”通知。在 Notifications 设计指南中有更具体的描写叙述。
注解:这样的Gmail特性 须要”收件箱“的大视图样式, 它是展开通知特征的一部分。自Android 4.1起可用。
以下的章节解说了怎样更新通知和移除通知。
更新通知
创建一个通知并通过调用 NotificationManager.notify(ID, notification) 和通知ID公布通知。这样就能更新通知了。要想更新你以前公布的通知。能够更新或创建一个 NotificationCompat.Builder 对象。然后用它构建一个 Notification 对象,使用你先前用过的同样ID公布这个 Notification 就能够了。假设先前的通知依旧可见,系统会更新 Notification 对象的内容。假设先前的通知已经消失了,就会新建一个通知。
以下的代码演示了更新一个通知来反映已发生事件的个数。
它把通知折叠起来,仅仅显示一个概要:
mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// 为通知设置一个ID以便能够更新它
int notifyID = 1;
mNotifyBuilder = new NotificationCompat.Builder(this)
.setContentTitle("New Message")
.setContentText("You've received new messages.")
.setSmallIcon(R.drawable.ic_notify_status)
numMessages = 0;
// 開始循环处理数据并通知用户
...
mNotifyBuilder.setContentText(currentText)
.setNumber(++numMessages);
// 因为ID保持不变。已存在的通知被更新了
mNotificationManager.notify(
notifyID,
mNotifyBuilder.build());
...
就会像这样产生一个通知:
图5.更新在通知抽屉里显示的通知。
移除通知
当下列情形之中的一个发生时通知就不再可见:
- 用户单个取消通知或通过使用“清除所有”取消通知(假设通知能够被清除的话)。
- 用户点击通知而且当你创建通知时调用了 setAutoCancel()。
- 你为一个指定的通知ID调用了
cancel()
。这种方法也会删除进行中的通知。 - 你调用了
cancelAll()
,这样就会移除你先前公布的所有通知。
启动Activity时维持导航
当你从一个通知里启动一个 Activity 时,你必须维持用户期望的导航体验。
点击回退键应该把用户从应用正常工作流带回到主屏幕而且点击近期打开应用能够看到这个 Activity 作为一个独立的任务显示。为了维持导航体验,你应该在一个全新的任务栈中启动 Activity
。怎样创建 PendingIntent 来获得一个新的任务栈依赖于你正在启动的 Activity 的性质。有两种概况:
- 普通的activity
-
你正要开启一个属于应用的正常工作流一部分的
Activity
。在这样的情况下,创建 PendingIntent 来开启一个新的任务栈并把应用的正常的回退行为拷贝到一个回退栈中供 PendingIntent 使用。 -
Gmail应用里的通知演示了这样的效果。
当你点击通知查看单条邮件信息时。你会直接看到信息本身。
按下回退键将会带你穿过Gmail回到主屏幕,恰如你是从主屏幕进入到Gmail的而不是通过通知进入的。
当你点击通知时无论你是不是在应用中都会像这样。
比如。假设你正在Gmail里编辑信息时点击一个查看单条邮件的通知,你会立马进入那条信息的详情界面。点击回退键会把你带回到收件箱然后到主屏幕,而不是你刚才正在编辑消息的界面。
- 特别的activity
-
假设它是从通知启动的那么用户仅仅会看到这个 Activity。从某种意义上讲。
Activity
通过展示那些非常难在通知里显示的信息扩展了通知。针对这样的情况,创建PendingIntent
来开启一个新的任务栈,但是不须要创建回退栈,由于已启动的 Activity 不属于应用的activity流的一部分,点击回退键会直接把用户带回主屏幕。
创建一个普通的activity PendingIntent
请依照下列步骤来创建一个能够直接启动 Activity 的 PendingIntent
:
- 在清单中定义应用的
Activity
层级。- 为Android 4.0.3或更早的版本号加入支持。通过在
里加入一个 子元素来为你准备启动的 Activity 指定父级来实现。 对于这个元素,设置
android:name="android.support.PARENT_ACTIVITY"
和android:value="
。"
是用来指定父级元素的 android:name 的值。參见以下的XML事例。 - 相同要为Android 4.1或更高的版本号加入支持。
在你要启动的 Activity 的
元素里加入 android:parentActivityName 属性来实现。
终于的XML应该像这样:
android:name=".MainActivity" android:label="@string/app_name" > android:name="android.intent.action.MAIN" /> android:name="android.intent.category.LAUNCHER" /> android:name=".ResultActivity" android:parentActivityName=".MainActivity"> android:name="android.support.PARENT_ACTIVITY" android:value=".MainActivity"/> - 为Android 4.0.3或更早的版本号加入支持。通过在
- 基于启动 Activity 的 Intent 创建一个回退栈:
- 创建启动 Activity 的 Intent。
- 通过调用 TaskStackBuilder.create() 创建stack builder。
- 通过调用 addParentStack() 来把回退栈加入到stack builder中。对于你在清单中定义的层级中的每个 Activity,回退栈包括了启动这个 Activity 的 Intent 对象。这种方法相同加入了在一个新的任务栈中开启回退栈的标识。
注解:虽然 addParentStack() 的參数是将要启动的 Activity 的引用,这种方法被调用时却不加入开启这个 Activity 的 Intent,而是在下一步实现。
- 通过调用 addNextIntent() 把启动这个 Activity 的 Intent 加入进来。
把你在第一步里创建的
Intent
作为參数传递给addNextIntent()
。 - 假设须要的话。通过调用 TaskStackBuilder.editIntentAt() 为堆栈上的
Intent
对象加入參数。有时候确保目标 Activity 显示有意义的数据是非常必要的,比如当用户使用回退键导航到它时。
- 通过调用 getPendingIntent() 为这个回退栈获取
PendingIntent
。然后你能够用这个
PendingIntent
作为setContentIntent()
的參数使用。
以下的代码片段演示了这个过程:
... Intent resultIntent = new Intent(this, ResultActivity.class); TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); // 加入回退栈 stackBuilder.addParentStack(ResultActivity.class); // 把Intent加入到栈顶 stackBuilder.addNextIntent(resultIntent); // 获取包括整个回退栈的 PendingIntent 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 PendingIntent
。
特别的 Activity
不须要回退栈,所以你不必在清单中定义它的 Activity 层级,且不须要调用 addParentStack() 构建回退栈。而是使用清单来创建 Activity 任务栈选项,这样通过调用 getActivity() 来创建 PendingIntent
:
- 在清单文件里。为 Activity 的
元素加入下列属性
-
android:name="activityclass"
- activity的全限定类名。
-
android:taskAffinity=""
-
结合你在代码中设置的
FLAG_ACTIVITY_NEW_TASK
标识就能够确保这个 Activity 不会进入应用默认的任务栈。不论什么拥有应用默认affinity的已存在的任务栈都不会受到影响。 -
android:excludeFromRecents="true"
- 从近期使用列表里排除这个新的任务栈防止用户一不小心回退到它。
This snippet shows the element:
android:name=".ResultActivity" ... android:launchMode="singleTask" android:taskAffinity="" android:excludeFromRecents="true"> ... -
- 构建并公布通知:
- 创建启动 Activity 的 Intent。
- 通过调用 setFlags() 并使用
FLAG_ACTIVITY_NEW_TASK
和FLAG_ACTIVITY_CLEAR_TASK
标识符为这个 Activity 开启一个新的且空的任务栈。 - 为这个 Intent 设置其它你须要的參数。
- 通过调用 getActivity() 从这个 Intent 创建一个
PendingIntent
。然后你就能把这个
PendingIntent
作为setContentIntent()
的參数使用。
以下的代码片段演示了这个过程:
// 实例化一个Builder对象。 NotificationCompat.Builder builder = new NotificationCompat.Builder(this); // 为这个Activity创建一个Intent Intent notifyIntent = new Intent(new ComponentName(this, ResultActivity.class)); // 为这个Activity设置开启一个新的且空的任务栈 notifyIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK); // 创建PendingIntent PendingIntent notifyIntent = PendingIntent.getActivity( this, 0, notifyIntent PendingIntent.FLAG_UPDATE_CURRENT ); // 把 PendingIntent 填充到通知的builder里 builder.setContentIntent(notifyIntent); // 通过把通知发送到NotificationManager系统服务中来公布通知。 NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); // 使用builder构建匿名的通知对象,然后传递给NotificationManager mNotificationManager.notify(id, builder.build());
在通知中显示运行进度条
通知能够包括一个活动的进度条来向用户显示正在进行中的操作的状态。
假设你能预计操作会运行多久和不论什么时刻完毕了多少的话,使用“确定时长的”指示器 (进度条)。假设你不能预计操作的运行时长,使用“不确定时长的”指示器(活动指示器)。
使用平台的 ProgressBar 类的扩展来显示运行进度条指示器。
在Android 4.0以上的平台使用进度条指示器,调用 setProgress()
。对于更早的版本号,你必须创建自己的包括 ProgressBar 视图的自己定义通知布局。
以下的章节讲述了怎样使用 setProgress() 在通知中显示运行进度条。
显示固定时长的进度条指示器
调用 setProgress() setProgress(max, progress, false) 把进度条加入到通知中然后公布就能显示一个确定时长的进度条了。
在操作运行时。添加progress并更新通知。在操作运行到最后,progress
应该达到max
。
一个通用的调用 setProgress() 的方式是把max
设置为100且把增量progress
作为操作“完毕度”的值。
你能够在操作完毕时依然显示运行进度条或移除它。
不管哪种方式,记得去更新通知文本去表示操作已经完毕了。调用 setProgress() setProgress(0, 0, false) 来移除进度条。比如:
...
mNotifyManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mBuilder = new NotificationCompat.Builder(this);
mBuilder.setContentTitle("Picture Download")
.setContentText("Download in progress")
.setSmallIcon(R.drawable.ic_notification);
// 在后台线程运行一个漫长的操作
new Thread(
new Runnable() {
@Override
public void run() {
int incr;
// 运行这个“漫长的”操作20次
for (incr = 0; incr <= 100; incr+=5) {
// 设置进度条指示器的最大值,当前完毕度和“确定时长的”状态
mBuilder.setProgress(100, incr, false);
// 首次显示运行进度条。
mNotifyManager.notify(0, mBuilder.build()); // 线程休眠,模拟操作耗费时间 try { // 休眠5秒钟 Thread.sleep(5*1000); } catch (InterruptedException e) { Log.d(TAG, "sleep failure"); } } // 当循环运行结束,更新通知 mBuilder.setContentText("Download complete") // 移除进度条 .setProgress(0,0,false); mNotifyManager.notify(ID, mBuilder.build()); } } // 在Runnable里调用run()方法运行线程 ).start();
通知结果如图6所看到的。左边的是操作运行期间通知的截图,右边的是操作完毕后的截图。
图6.运行期和运行后的通知。
显示持续的活动指示器
使用 setProgress(0, 0, true) (前两个參数能够忽略)把活动指示器加入到通知中然后公布就能够显示一个不确定时长的活动指示器。终于的指示器和进度条有一样的样式。仅仅是它的动画始终在运行。
在操作開始时公布通知。动画会一直持续到你改动通知。
在操作运行完时调用 setProgress() setProgress(0, 0, false)
然后更新通知来移除活动指示器。请确保始终这样运行,否则在操作完毕时动画还会继续运行。别忘了更新通知文本去表示操作已经完毕了。
了解活动指示器怎样工作,參考前面的代码片段。查找下面行:
// 设置进度条指示器的最大值。当前完毕度和“确定时长的”状态
mBuilder.setProgress(100, incr, false);
// 公布通知
mNotifyManager.notify(0, mBuilder.build());
把你找到的这些行替换成下面行:
// 为不确定时长的操作设置活动指示器
mBuilder.setProgress(0, 0, true);
// 公布通知
mNotifyManager.notify(0, mBuilder.build());
通知结果如图7所看到的:
图7.正在运行的活动指示器
自己定义通知布局
通知框架同意你在 RemoteViews 对象里定义通知的外观来自己定义通知的布局。
自己定义通知与普通通知非常像。仅仅是他们是基于在XML布局文件里定义的 RemoteViews
。
自己定义 通知布局的可用高度依赖于通知视图的类型。
普通视图布局限制在64dp内。展开视图的布局限制在256dp内
定义自己定义通知布局,首先要实例化一个能够填充XML布局文件的 RemoteViews 对象。然后调用 setContent() 而不是类似 setContentTitle()
的方法。通过使用 RemoteViews 里的方法设置视图子元素的值来设置自己定义通知的内容详情:
- 在一个单独的文件里为通知创建XML布局。你能够使用不论什么你想用的名字,可是必须以.xml结尾。
- 在你的应用中,使用
RemoteViews
的方法定义通知的图标和文本。通过调用 setContent() 把这个RemoteViews
对象放入你的NotificationCompat.Builder
中。要避免为你的 RemoteViews 对象设置背景 Drawable 图片,由于这样你的文本颜色可能变得无法辨别。
RemoteViews
类相同也包括你能够非常easy在你的通知布局中加入 Chronometer 或 ProgressBar 的方法。请參考 RemoteViews 參考文档了解很多其它关于为通知创建自己定义布局的信息。
注意:当你使用自己定义通知布局时,请特别注意要确保你的自己定义布局可以在不同方向和分辨率的设备上工作。尽管这条建议适用于全部的视图布局,对于通知来说尤其重要。由于通知抽屉里的空间要求很严格。不要把自己定义布局弄的太复杂而且要保证在不同配置的设备上測试。
为自己定义通知的文本使用样式资源
始终为自己定义通知的文本使用样式资源。在不同设备和不同版本号的平台上通知的背景颜色会有差异,使用样式资源能够帮助你解决问题。从Android 2.3開始,系统为标准通知布局的文本定义了样式。假设你在目标版本号为Android 2.3或更高的应用中使用同样的样式,你能够确保你的文本是差别于显示的背景色可见。