[翻译]Android官方文档 - 通知(Notifications)
翻译的好辛苦,有些地方也不太理解什么意思,如果有误,还请大神指正。
官方文档地址:http://developer.android.com/guide/topics/ui/notifiers/notifications.html
notification可以作为一条信息被显示在你的应用程序的正常UI之外。当你告诉系统去发布一条notification,它首先会在notification area(通知区域)显示一个小图标。想要去查看notification的详细内容,用户必须打开notification drawer(通知抽屉)。notification area和notification drawer都是由系统控制的区域,用户可以在任何时候查看它们。
图1.通知在通知区域(notification area)
图2.通知在通知抽屉(notification drawer)
通知设计(Notification Design)
通知,作为Android UI中重要的一部分,有它自己的设计指南。想要学习如何设计通知和它们的交互,请阅读Android设计指南(Android Design Guide) Notifications部分。
笔记:除非特别说明,本指南所引用的NotificationCompat.Builder 类是在第4版本的 Support Library. 类 Notification.Builder 在 Android 3.0被添加。
通知显示元素(Notification Display Elements)
通知在通知抽屉里能使用两种视觉样式中的一种,同时也取决于抽屉的版本与状态。(不同版本的Android的抽屉会有不同的视觉效果)
正常视图(Normal View)
通知的标准视图显示于通知抽屉中。
大视图(Big View)
当通知被扩展时,可以看见一个更大的视图。大视图只有自Android 4.1以来已拓展的通知特性的一部分。
它们的样式与描述将在下面给出。
正常视图(Normal View)
一条在正常视图中的通知显示有64dp高。虽然你创建了一个大视图的通知,但它还是会先显示正常的尺寸,直到你展开它。下面是一个正常视图的例子。
图3.在正常视图中的通知。
插图中的编号请看如下的参考:
1.内容标题(Content title)
2.大图标(Large icon)
3.内容文本(Content text)
4.内容信息(Content info)
5.小图标(Small icon)
6.通知被发布的时间,你可以通过 setWhen()来设定一个指定的值;如果你不设定的话,它将默认是系统接受到这条通知的时间。
大视图(Big View)
一个通知的大视图只有在它被展开的时候显示,这发生在通知在通知抽屉的顶部(译者注:即,通知抽屉中,第一条会自动展开。),或者当用户通过手势去展开它。展开的通知从Android 4.1开始
可用。
接下来的截屏展示了一个inbox-style的通知:
图4.大视图的通知。
通知在大视图中有着和普通视图差不多的视觉元素。这唯一的不同是标号7的地方,详细区域。每一个大视图需要设置这块区域通过不同方式。可用的样式有:
大图片样式(Big picture style)
这个详细区域包含一个256dp高的位图。
大本文样式(Big text style)
在这个区域显示一大段文本。
收件箱样式(Inbox style)
在这个区域显示一行一行的文本。
大视图的样式也有如下这些可选的,它们在正常视图中不可用:
大内容标题(Big content title)
允许你去覆写正常视图的内容标题,那个标题只在展开的视图中显示。
文本摘要(Summary text)
允许你在详细区域之上添加一行文本。
把大视图的样式应用到通知上在这里有详细的描述Applying a big view style to a notification。
创建一条通知(Creating a Notification)
你可以使用NotificationCompat.Builder对象为一条通知来指定UI的信息和行为。要创建以一个notification对象,你可以调用NotificationCompat.Builder.build(),它将返回一个Notification对象,包含着你指定的属性。要去发布这条通知,可以通过调用NotificationManager.notify()来发送它。
通知所必须的内容(Required notification contents)
一条通知对象必须包含如下内容:
- 一个小图标,通过setSmallIcon()来设定。
- 一个标题,通过setContentTitle()来设定。
- 详细文本,通过setContentText()来设定。
所有其它的内容和设置都是可选的。可以查看文档NotificationCompat.Builder来学习更多的内容。
通知的行为(Notification actions)
虽然它们是可选的,但你至少给你的通知添加一个行为。一个行动允许用户直接从通知跳转到你应用里的一个activity里。
一条通知可以提供多条事件。你应该定义一个行为去响应用户点击通知的时候。通常行为会打开你应用程序里的一个Activity。你也可以给通知添加额外的按钮去完成更丰富的操作,想暂停一个闹钟,或者回复一个短消息。这种特性在Android 4.1可用。如果你使用了额外的按钮,你必须确保它们的功能在Activity中也是可用的。查看Handling compatibility获取更多细节。
在一个通知里面,这个行为通过PendingIntent来定义,里面包含着一个从你应用的Activity启动的Intent。通过手势,调用NotificationCompat.Builder适当的方法来关联PendingIntent。打个比方,如果你想要去启动一个Activity,当用户点击通知抽屉里面的通知文本的时候,通过调用setContentIntent()来添加PendingIntent。
大部分的行为策略都是当用户点击通知的时候启动一个Activity。你也可以当用户取消通知的时候开始Activity。在Android 4.1和之后的版本,你可以通过行为按钮来启动Activity。想要了解更多,参照NotificationCompat.Builder。
创建一个简单的通知(Creating a simple notification)
下面的代码片段举例说明了一个通知当用户点击它的时候就打开一个指定的activity。注意代码中使用的TaskStackBuilder对象,以及如何使用它为行为创建PendingIntent。关于这种模式的更多细节请参考Preserving Navigation when Starting an Activity。
NotificationCompat.Builder mBuilder = newNotificationCompat.Builder(this) .setSmallIcon(R.drawable.notification_icon) .setContentTitle("My notification") .setContentText("Hello World!"); // 为Activity创建一个明确的Intent Intent resultIntent =newIntent(this,ResultActivity.class); // 栈建造者对象将为启动的Activity制造一个人工的回退栈 // 这样确保了在按回退键的时候,可以从启动的Activity回退到主屏幕上 TaskStackBuilder stackBuilder =TaskStackBuilder.create(this); // 为Intent添加回退栈,但不是Intent本身 stackBuilder.addParentStack(ResultActivity.class); // 添加Intent,这个Intent启动Activity并放置它到栈顶 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());
到这里为止,你的用户已经被通知了。
给通知应用一个大视图样式(Applying a big view style to a notification)
当通知被展开的时候,会有一个大的视图出现,首先创建一个NotificationCompat.Builder对象来添加一些你想要的可选的样式。然后调用Builder.setStyle(),把big view style对象作为参数传入。
记住,展开通知在Android 4.1之前是不可用的。想要学习如何在Android 4.1以前更早的版本上处理这个问题,参考Handling compatibility。
举个例子,接下来的代码片段示范了如何修改之前创建的通知,让它使用一个收信箱大视图样式:
NotificationCompat.Builder mBuilder =newNotificationCompat.Builder(this) .setSmallIcon(R.drawable.notification_icon) .setContentTitle("Event tracker") .setContentText("Events received") NotificationCompat.InboxStyle inboxStyle = newNotificationCompat.InboxStyle(); String[] events =newString[6]; // 给收信箱样式的大视图设置一个标题 inboxStyle.setBigContentTitle("Event tracker details:"); ... // 将事件列表放入收信箱中 for(int i=0; i < events.length; i++){ inboxStyle.addLine(events[i]); } //将收信箱样式设置给通知对象 mBuilder.setStyle(inBoxStyle); ... // 在这里写上发布通知的代码
处理兼容性(Handling compatibility)
对于某个版本来说,并不是所有的通知的特性都是可用的,尽管在支持库中的类NotificationCompat.Builder是可以设置这些方法。比如说,行为按钮(action button),这取决于展开通知,只出现Android 4.1及更高的版本上,因为可展开的通知只在Android 4.1及更高的版本可以被使用。
为了确保最好的兼容性,使用NotificationCompat来创建通知对象,特别推荐使用NotificationCompat.Builder。另外,在实现你的通知对象的时候请遵从下面的步骤:
- 为所有的用户提供通知的所有功能,不管他们正在使用哪个版本的Android。为了能够做到这一点,在Activity中验证所有的功能都是可用的。如果必要的话,你可能需要添加一些新的Activity去完成这个工作。
- 确保所有的功能都能在Activity中获取到。通过让通知启动Activity,为了做到这一点,为Activity创建PendingIntent,调用setContentIntent()把PendingIntent添加到通知对象上。
- 现在添加你想要在通知上使用的特性,记住,任何你想要的功能都可以通过点击通知启动Activity来实现。
当你需要为同样的事件发布多次通知的时候,你应该避免每次都完全的构建一个新的通知,取而代之的,你应该考虑更新之前的通知,或者也可以改变,添加一些值。
比方说,Gmail通过增加未读信息的数量和添加每条信息的概况来通知用户有新的邮件送达了。这被称为“堆砌“通知,详细信息请参考Notifications。
笔记:Gmail的特性要求收信箱的样式,这个样式在可展开通知中在Android 4.1后可用。
接下来的内容描述了如何更新通知和如何删除它们。
更新通知(Updating notifications)
可以设置一条通知,当然可以更新一条通知,我们通过在调用NotificationManager.notify(ID, notification)时所使用的ID来更新它。为了更新你之前发布的通知,你需要更新或者创建一个NotificationCompat.Builder对象,从之前的通知中创建一个Notification对象,然后发布它使用你之前使用的ID。如果之前的通知仍然可见,系统将直接更新通知的内容,如果之前的通知不可见了,一条新的通知将被创建。
下面的代码片段示范了一条通知是如何被更新的,去反映已经发生的事件的数量,它堆叠着通知,显示一个概括:
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); // 给通知设置一个ID,这个可以用来更新 int notifyID =1; mNotifyBuilder =newNotificationCompat.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.在显示的通知抽屉中更新通知
移除通知(Removing notifications)
通知会一直存在,直到下面的情况之一发生:
- 用户手动一条一条的清除通知,或者使用”清除所有“(如果通知允许被清除)。
- 用户点击通知,当你创建这条通知的时候使用了setAutoCancel()。
- 你为一个指定ID的通知调用了cancel(),这个方法也去删除正在执行的通知。
- 你调用了cancelAll()方法,这将移除所有你之前发布的通知。
当你从一条通知启动一个Activity,你必须保留用户预期的导航栏经历。点击Back按钮,应该带用户回到应用的正常工作的样子。点击最近的任务,应该把Activity作为单独的任务显示。为了能保留用户的导航经历,你应该在一个全新的任务中开启Activity。你如何去设置PendingIntent去给你一个全新的任务,取决于你启动的Activity的性质,这里有两种常见的情况:
正常的Activity
你正在启动的Activity是属于应用程序正常工作流程中的一部分。在这种情况下,设置PendingIntent去开始一个全新的task,并且为回退栈提供一个PendingIntent去响应应用程序正常的回退事件。
来自Gmail应用的通知示范了这一点。当你点击通知中单独的一条邮件信息的时候,你将看到信息本身,触碰回退键,将带你经过Gmail到主屏幕,就好像你刚刚从主屏幕进入Gmail一样,而不是从通知进去的。
除非你已经在应用里面再点通知的,否则这都会发生,比如说,你正在Gmail里面写信息,然后你点击了通知里面的一条信息,你将直接进入那条邮件,触碰回退键将带你到收信箱,再到主屏幕。而不是带你到刚刚写信息的地方。
特殊的Activity
用户只会看见这个Activity,如果它是从通知里启动的。从某种意义上说,Activity通过提供一些很难在通知里显示的信息拓展了通知。在这种情况下,设置PendingIntent去开始一个新的任务,这就不需要去创建一个回退栈。因为,启动Activity并不是应用程序的一部分。点击回退按钮将直接回到主屏幕。
设置一个常规的Activity PendingIntent(Setting up a regular activity PendingIntent)
想要设置一个PendingIntent能直接进入Activity,请按照以下步骤:
- 在manifest文件中你应用程序的Activity层级。
为这个元素设置android-name="android.support.PARENT_ACTIVITY"。设置android-value="<parent_activity_name>",这个<parent_activity_name>的值是父级Activity中的android-name的值。下面的XML有举例说明。
b.也要给Android 4.1和之后的版本添加支持。为了做到这一点,给你启动的Activity的<activity>中添加android-parentActivityName。
最终的xml看起来是这样的:
<activity android:name=".MainActivity" android:label="@string/app_name"> <intent-filter> <actionandroid:name="android.intent.action.MAIN"/> <categoryandroid: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>
b.通过调用TaskStackBuilder.create()去创建栈建造者。
c.通过调用addParentStack()去添加回退栈。对于你在manifest文件中定义的每一个在层级结构中的Activity,回退栈包含一个启动Activity的Intent对象。这个方法也在一个全新的任务中添加了一个开始栈的标识符。
e.如果你需要,通过调用TaskStackBuilder.editIntentAt()给Intent对象传递参数。当用户使用Back按键来导航,有时候非常有必要确保目标Activity显示有意义的数据。
下面的代码片段示范了这个过程。
... Intent resultIntent =newIntent(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 =newNotificationCompat.Builder(this); builder.setContentIntent(resultPendingIntent); NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mNotificationManager.notify(id, builder.build());
设置一个特殊的Activity PendingIntent(Setting up a special activity pendingIntent)
接下来的几个部分描述了如何设置一个特殊的ActivityPendingIntent。
一个特殊的activity不需要回退栈,所以你可以不在manifest文件中定义activity的层级关系,你也不需要在构建回退栈的时候调用addParentStack()。取而代之的,使用manifest文件来设置activity的任务选项,通过getActivity()来创建PendingIntent。
1.在你的manifest文件中,为你的Activity添加如下属性
android-name="activityclass"
activity的完整类名。
与你在代码中的FLAG_ACTIVITY_NEW_TASK标识符有关,这确保了Activity不会进入应用程序的任务中。任何与应用程序默认的任务都不会受到影响。
android-excludeFromRecents="true"
排除来自“最近任务”的新任务,所以用户不会不小心导航回去。
下面代码片段显示这个xml代码
<activity android:name=".ResultActivity" ... android:launchMode="singleTask" android:taskAffinity="" android:excludeFromRecents="true"> </activity> ...
2.构建和发布通知:
b.通过调用setFlags()来设置Activity在一个全新的,空的任务中启动,可以使用如下标识符作为参数:FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TASK。
c.为Intent设置其它你需要的可选项。
下面的代码片段示范了这个过程。
// 实例化一个建造对象 NotificationCompat.Builder builder =newNotificationCompat.Builder(this); // 为Activity创建一个Intent Intent notifyIntent = newIntent(newComponentName(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.setContentIntent(notifyIntent); // 通知通过通知管理器发布 NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); // 构建一个匿名的通知发送给通知管理器 mNotificationManager.notify(id, builder.build());
在通知里显示一个进度条(Displaying Progress in a Notification)
通知可以包含一个动态的进度指示器,用来显示当前操作的进度。如果你能估计这个操作总共有多久,以及任何时间点上的完成度,那么你可以用一个“确定”格式的指示器(进度条)。如果你没法估计操作的长度,用“不确定”格式的指示器。
进度条指示器是实现了ProgressBar来执行显示的。
调用setProgress()方法来使用进度条指示器开始于Android 4.0.在之前的版本,你必须创建自己的通知布局来包含ProgressBar视图。
接下来的部分介绍了如何使用setProgress()来在通知中显示进度条。
显示一个已知长度的进度条指示器(Displaying a fixed-duration progress indicator)
为了能够显示一个确定的进度条,通过调用setProgress() setProgress(max, progress, false)给你的通知加上进度条。然后发布通知。然后,随着操作的进度,增加进度值,然后更新通知。在操作结束的时候,进度值应该等于最大值。通常的方式是调用setProgress()来设置最大值为100,然后去增加进度完成的百分比。
你可以在操作完成的时候显示进度条,也可以移除掉它。在这样的情况下,记住要去更新通知的文本,显示操作已经完成了。调用setProgress() 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); // 在后台线程开启一个耗时操作 newThread( newRunnable(){ @Override publicvoid 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()); } } // 开始这个线程 ).start();
效果显示在图6上。左边的截图上是通知正在执行这操作,右边的截图是通知已经完成了操作。
图6.在工作中的进度条与完成后的进度条
显示一个持续的活动指示器(Displaying a continue activity indicator)
为了能使用不确定的活动指示器,使用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.一个持续的活动指示器。
自定义通知布局(Custom Notification Layouts)
通知的框架允许你去自定义通知的布局。通过RemoteViews对象来定义通知的外观。自定义通知布局与常规通知相似,但是它是基于定义在XML文件的RemoteViews对象来操作的。
自定义通知的可用高度是取决于通知视图的。正常的视图布局高度限制在64dp,可展开视图的布局高度限制在256dp。
为了去定义自己的通知布局,从扩充XML文件获取一个RemoteViews对象的实例开始。然后,类似于调用setContentTitle()方法一样,我们需要调用setContent()。为了能设置更多细节,我们使用RemoteViews对象的方法来设置更多的内容。
1.创建一个单独的XML文件,用来定义通知的布局。你可以使用任何你想用的名字,但后缀必须是.xml。
2.在应用里面,使用RemoteViews对象的方法来给你的通知设置文本和图标,通过调用setContent()把你的RemoteViews对象放到NotificationCompat.Builder里面。避免使用背景图像,因为你的文本可能会变得不太好阅读。
注意:当你使用自定义的通知条的时候,特别要注意你自定义的通知条在不同方向与分辨率的设备上是如何工作的。当然这条建议对所有的视图布局都很重要。但对通知条来说是尤其重要的,因为通知抽屉的控件十分的有限。不要把自己的通知条做的太复杂,确保它的灵活性。
为自定义的通知条文本使用样式资源(Using style resources for custom notification text)
自定义通知条的时候总是使用样式资源去定义文本。通知的背景颜色会变得与设备与当前版本的android有很大的反差。使用样式文件能帮你很好的解决这一点。从Android 2.3开始,系统就为标准的通知布局定义了文本的样式,如果你在Android2.3 以及其更高的版本上使用同样的样式,你必须确保你的文本相对于背景是可以看见的。