九、创建不同样式的通知
1、创建基本样式的通知
a.基本通知
最基本、最紧凑的形式(也称为“折叠形式”)的通知会显示一个图标、标题和少量文本内容。
设置通知的内容
使用 NotificationCompat.Builder
对象设置通知的内容和渠道。
小图标,通过
setSmallIcon()
设置。这是所必需的唯一用户可见内容。标题,通过
setContentTitle()
设置。正文文本,通过
setContentText()
设置。通知优先级,通过
setPriority()
设置。优先级决定了 Android 7.1 及更低版本上的通知的干扰程度。对于 Android 8.0 及更高版本,请改为设置渠道重要性,如下一部分所示。
var builder = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle(textTitle)
.setContentText(textContent)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
NotificationCompat.Builder 构造函数要求您提供渠道 ID。这是与 Android 8.0(API 级别 26)及更高版本兼容所必需的,但被早期版本忽略。
创建渠道并设置重要性
需要先将 NotificationChannel
的实例传递给 createNotificationChannel()
,在系统中注册应用的通知渠道,然后才能在 Android 8.0 及更高版本上发送通知。
private fun createNotificationChannel() {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is not in the Support Library.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = getString(R.string.channel_name)
val descriptionText = getString(R.string.channel_description)
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
description = descriptionText
}
// Register the channel with the system.
val notificationManager: NotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
必须先创建通知渠道,然后才能在 Android 8.0 及更高版本上发布任何通知,因此请在应用启动时立即执行此代码。您可以放心地重复调用此方法,因为创建现有通知渠道不会执行任何操作。
NotificationChannel
构造函数需要 importance
,它使用 NotificationManager
类中的一个常量。此参数确定对于属于此渠道的任何通知,如何干扰用户。将优先级设置为 setPriority()
以支持 Android 7.1 及更低版本。
必须设置通知的重要性或优先级,如以下示例所示,但系统不保证您会获得提醒行为。在某些情况下,系统可能会根据其他因素更改重要性级别,并且用户可以随时重新定义给定渠道的重要性级别。
设置通知的点按操作
每个通知都必须响应点按操作,通常是为了打开应用中与通知对应的 activity。为此,请指定使用 PendingIntent
对象定义的内容 intent,并将其传递给 setContentIntent()
。
// Create an explicit intent for an Activity in your app.
val intent = Intent(this, AlertDetails::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE)
val builder = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle("My notification")
.setContentText("Hello World!")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
// Set the intent that fires when the user taps the notification.
.setContentIntent(pendingIntent)
.setAutoCancel(true)
此代码会调用 setAutoCancel()
,它会在用户点按通知时自动移除通知。
上例中显示的 setFlags()
方法在用户使用通知打开您的应用后保留了预期的导航体验。您可能需要根据要启动的 activity 类型(可以是以下类型之一)来使用它:
专门用于响应通知的 activity。用户在正常使用应用期间不会无缘无故地导航到此 activity,因此该 activity 会启动新任务,而不是添加到应用的现有任务和返回堆栈。这是在上述示例中创建的 intent 类型。
应用的常规应用流程中存在的 Activity。在这种情况下,启动 activity 会创建一个返回堆栈,以便保留用户对返回和向上按钮的预期。
从通知启动 Activity
常规 Activity
这类 Activity 是应用的正常用户体验流程的一部分。当用户从通知转到 activity 时,新任务必须包含完整的返回堆栈,以便用户点按返回按钮在应用层次结构中向上导航。
特殊 Activity
只有当 Activity 从通知启动时,用户才可以看到此类 Activity。从某种意义上讲,此 activity 通过提供通知本身难以显示的信息来扩展通知界面。此 activity 不需要返回堆栈。
设置常规 Activity PendingIntent
如需从通知启动常规 activity,请使用 TaskStackBuilder
设置 PendingIntent
,以便它可以创建新的返回堆栈,如下所示。
定义应用的 Activity 层次结构
通过向应用清单文件中的每个 <activity>
元素添加 android:parentActivityName
属性,定义 activity 的自然层次结构。
<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>
<!-- MainActivity is the parent for ResultActivity. -->
<activity
android:name=".ResultActivity"
android:parentActivityName=".MainActivity" />
...
</activity>
构建包含返回堆栈的 PendingIntent
如需启动包含 activity 返回堆栈的 activity,请创建一个 TaskStackBuilder
实例并调用 addNextIntentWithParentStack()
,并向其传递您要启动的 activity 的 Intent
。
只要您按照前文所述为每个 activity 定义父 activity,就可以调用 getPendingIntent()
来接收包含整个返回堆栈的 PendingIntent
。
// Create an Intent for the activity you want to start.
val resultIntent = Intent(this, ResultActivity::class.java)
// Create the TaskStackBuilder.
val resultPendingIntent: PendingIntent= TaskStackBuilder.create(this).run {
// Add the intent, which inflates the back stack.
addNextIntentWithParentStack(resultIntent)
// Get the PendingIntent containing the entire back stack.
getPendingIntent(0,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
}
可以通过调用 TaskStackBuilder.editIntentAt()
向堆栈中的 Intent
对象添加参数。有时必须这样做,才能确保返回堆栈中的 activity 在用户导航到该 activity 时显示有意义的数据。
val builder = NotificationCompat.Builder(this, CHANNEL_ID).apply {
setContentIntent(resultPendingIntent)
...
}
with(NotificationManagerCompat.from(this)) {
notify(NOTIFICATION_ID, builder.build())
}
设置特殊 Activity PendingIntent
由于从通知启动的特殊 activity 不需要返回堆栈,因此您可以通过调用 getActivity()
来创建 PendingIntent
。不过,请在清单中定义相应的任务选项。
- 在清单中,将以下属性添加到
<activity>
元素中。
android:taskAffinity= "" 与在代码中使用的 [FLAG_ACTIVITY_NEW_TASK]
标志结合使用,将此属性设置为空白,以确保此 activity 不会进入应用的默认任务。具有应用默认亲和性的任何现有任务都不会受到影响。
android:excludeFromRecents="true"`从“最近使用的应用”屏幕中排除新任务,以免用户意外返回该任务。
val notifyIntent = Intent(this, ResultActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val notifyPendingIntent = PendingIntent.getActivity(
this, 0, notifyIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
val builder = NotificationCompat.Builder(this, CHANNEL_ID).apply {
setContentIntent(notifyPendingIntent)
...
}
with(NotificationManagerCompat.from(this)) {
notify(NOTIFICATION_ID, builder.build())
}
b.添加操作按钮
通知最多可以提供三个操作按钮,以便用户快速响应,例如暂停提醒或回复短信。但这些操作按钮不得重复用户点按通知时执行的操作。
添加操作按钮,请将 PendingIntent
传递给 addAction()
方法。这就像是设置通知的默认点按操作,除了启动 activity 之外,您可以执行其他操作,例如启动在后台执行作业的 BroadcastReceiver
,这样该操作就不会中断已经打开的应用。
val ACTION_SNOOZE = "snooze"
val snoozeIntent = Intent(this, MyBroadcastReceiver::class.java).apply {
action = ACTION_SNOOZE
putExtra(EXTRA_NOTIFICATION_ID, 0)
}
val snoozePendingIntent: PendingIntent =
PendingIntent.getBroadcast(this, 0, snoozeIntent, 0)
val builder = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle("My notification")
.setContentText("Hello World!")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.addAction(R.drawable.ic_snooze, getString(R.string.snooze),
snoozePendingIntent)
如需详细了解如何构建 BroadcastReceiver
以运行后台工作,请参阅广播概览。
如果您尝试构建包含媒体播放按钮的通知(例如暂停和跳过曲目),请参阅如何创建包含媒体控件的通知。
注意 :在 Android 10(API 级别 29)及更高版本中,如果应用不提供自己的通知操作按钮,平台会自动生成通知操作按钮。如果您不希望应用通知显示任何建议的回复或操作,可以使用
setAllowGeneratedReplies()
和setAllowSystemGeneratedContextualActions()
选择停用系统生成的回复和操作。
c.添加直接回复操作
Android 7.0(API 级别 24)中引入的直接回复操作可让用户直接在通知中输入文本。然后,文本会在不打开 activity 的情况下传递给您的应用。例如,您可以使用直接回复操作,让用户能够在通知中回复短信或更新任务列表。
直接回复操作在通知中显示为用于打开文本输入的附加按钮。用户完成输入后,系统会将文本响应附加到您为通知操作指定的 intent,并将该 intent 发送到您的应用。
添加回复按钮
1.创建可添加到通知操作的 RemoteInput.Builder 实例。此类的构造函数接受系统用作文本输入键的字符串。您的应用稍后使用该键来检索输入的文本。
// Key for the string that's delivered in the action's intent.
private val KEY_TEXT_REPLY = "key_text_reply"
var replyLabel: String = resources.getString(R.string.reply_label)
var remoteInput: RemoteInput = RemoteInput.Builder(KEY_TEXT_REPLY).run {
setLabel(replyLabel)
build()
}
2.为回复操作创建 PendingIntent。
// Build a PendingIntent for the reply action to trigger.
var replyPendingIntent: PendingIntent =
PendingIntent.getBroadcast(applicationContext,
conversation.getConversationId(),
getMessageReplyIntent(conversation.getConversationId()),
PendingIntent.FLAG_UPDATE_CURRENT)
注意:如果重复使用
PendingIntent
,用户回复的对话可能不是他们预期的对话。您必须为每个对话提供不同的请求代码,或者提供一个当您对任何其他对话的回复 intent 调用 equals()时不会返回true
的 intent。对话 ID 会作为 intent 的 extra 捆绑包的一部分频繁传递,但在您调用equals()
时会被忽略。
3.使用 addRemoteInput() 将 RemoteInput 对象附加到操作。
// Create the reply action and add the remote input.
var action: NotificationCompat.Action =
NotificationCompat.Action.Builder(R.drawable.ic_reply_icon,
getString(R.string.label), replyPendingIntent)
.addRemoteInput(remoteInput)
.build()
4.对通知应用操作并发出通知。
// Build the notification and add the action.
val newMessageNotification = Notification.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_message)
.setContentTitle(getString(R.string.title))
.setContentText(getString(R.string.content))
.addAction(action)
.build()
// Issue the notification.
with(NotificationManagerCompat.from(this)) {
notificationManager.notify(notificationId, newMessageNotification)
}
从回复中检索用户输入
如需从通知的回复界面接收用户输入,请调用 RemoteInput.getResultsFromIntent()
,并向其传递 BroadcastReceiver
收到的 Intent
:
private fun getMessageText(intent: Intent): CharSequence{
return RemoteInput.getResultsFromIntent(intent)?.getCharSequence(KEY_TEXT_REPLY)
}
处理文本后,通过调用 NotificationManagerCompat.notify() 并使用相同的 ID 和标记(如果使用的话)更新通知。如需隐藏直接回复界面,并向用户确认他们的回复已正确接收和处理,您必须这样做。
// Build a new notification, which informs the user that the system
// handled their interaction with the previous notification.
val repliedNotification = Notification.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_message)
.setContentText(getString(R.string.replied))
.build()
// Issue the new notification.
NotificationManagerCompat.from(this).apply {
notificationManager.notify(notificationId, repliedNotification)
}
在处理这个新通知时,请使用传递给接收器的 onReceive()
方法的上下文。
通过调用 setRemoteInputHistory()
将回复附加到通知底部。不过,如果您要构建即时通讯应用,请创建消息式通知,并将新消息附加到对话中。
如需了解有关即时通讯应用通知的更多建议,请参阅即时通讯应用最佳实践部分。
d.添加进度条
通知可以包含动画形式的进度指示器,向用户显示正在进行的操作状态。
如果您可以估算操作在任何时间的完成进度,请调用 setProgress(max, progress, false)
以使用指示符的“确定性”形式(如图 5 所示)。第一个参数是“complete”值,例如 100。第二个因素是完成程度。最后一个表示这是一个确定性进度条。
随着操作的继续,持续使用更新后的 progress
值调用 setProgress(max, progress, false)
并重新发出通知,如以下示例所示。
注意 :由于进度条要求应用持续更新通知,因此应使此代码在后台服务中运行。
...
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID);
builder.setContentTitle("Picture Download")
.setContentText("Download in progress")
.setSmallIcon(R.drawable.ic_notification)
.setPriority(NotificationCompat.PRIORITY_LOW);
// Issue the initial notification with zero progress.
int PROGRESS_MAX = 100;
int PROGRESS_CURRENT = 0;
builder.setProgress(PROGRESS_MAX, PROGRESS_CURRENT, false);
notificationManager.notify(notificationId, builder.build());
// Do the job that tracks the progress here.
// Usually, this is in a worker thread.
// To show progress, update PROGRESS_CURRENT and update the notification with:
// builder.setProgress(PROGRESS_MAX, PROGRESS_CURRENT, false);
// notificationManager.notify(notificationId, builder.build());
// When done, update the notification once more to remove the progress bar.
builder.setContentText("Download complete")
.setProgress(0,0,false);
notificationManager.notify(notificationId, builder.build());
操作结束时,progress 必须等于 max。您可以离开进度条以显示操作已完成,也可以将其移除。无论是哪种情况,都请更新通知文本以显示操作已完成。如需移除进度条,请调用 setProgress(0, 0, false)。
如需显示不确定的进度条(不指示完成百分比的进度条),请调用 setProgress(0, 0, true)。结果是,指示器的样式与前面的进度条相同,只不过它是一个不指示完成的连续动画。在您调用 setProgress(0, 0, false) 之前,进度动画会一直运行,然后更新通知以移除 activity 指示器。
请记得更改通知文本,以表明操作已完成。
注意 :如果您需要下载文件,请考虑使用
DownloadManager
,它会提供专属通知来跟踪下载进度。
e.设置系统范围的类别
Android 使用预定义的系统范围类别来确定当用户启用勿扰模式时,是否发出给定通知干扰用户。
如果您的通知属于 NotificationCompat
中定义的通知类别之一(例如 CATEGORY_ALARM
、CATEGORY_REMINDER
、CATEGORY_EVENT
或 CATEGORY_CALL
),请通过将相应类别传递给 setCategory()
进行声明:
var builder = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle("My notification")
.setContentText("Hello World!")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
系统会根据这些有关通知类别的信息来决定是否在设备处于“勿扰”模式时显示通知。但是,您无需设置系统范围的类别。只有当您的通知与 NotificationCompat 中定义的某个类别匹配时,您才应执行此操作。
f.显示紧急消息
您的应用可能需要显示紧急的时效性消息,例如来电或响铃的闹钟。在这些情况下,您可以将全屏 intent 与通知相关联。
注意 :包含全屏 intent 的通知具有非常强的干扰性,因此请务必只将此类通知用于最紧急的时效性消息。
调用通知后,用户会看到以下某种内容,具体取决于设备的锁定状态:
- 如果用户的设备处于锁定状态,系统会显示全屏 activity,并覆盖锁定屏幕。
- 如果用户的设备处于解锁状态,通知会以展开形式显示,其中包含用于处理或关闭通知的选项。
注意 :如果您的应用以 Android 10(API 级别 29)或更高版本为目标平台,您必须在应用的清单文件中请求
USE_FULL_SCREEN_INTENT
权限,系统才能启动与时效性通知相关联的全屏 activity。
val fullScreenIntent = Intent(this, ImportantActivity::class.java)
val fullScreenPendingIntent = PendingIntent.getActivity(this, 0,
fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT)
var builder = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle("My notification")
.setContentText("Hello World!")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setFullScreenIntent(fullScreenPendingIntent, true)
g.设置锁定屏幕公开范围
如需控制锁定屏幕中通知的详细可见信息,请调用 setVisibility()
并指定以下值之一:
取值 | 含义 |
---|---|
VISIBILITY_PUBLIC |
在锁定屏幕上显示通知的完整内容 |
VISIBILITY_SECRET |
锁定屏幕上不会显示通知的任何部分 |
VISIBILITY_PRIVATE |
锁定屏幕上仅显示基本信息,如通知图标和内容标题。未显示通知的完整内容 |
设置 VISIBILITY_PRIVATE
时,您还可以提供隐藏某些详细信息的备用版本通知内容。例如,短信应用可能会显示一条通知,指出“您有 3 条新短信”,但隐藏了短信内容和发送者。如需提供此备用通知,请先照常使用 NotificationCompat.Builder
创建备用通知。然后,使用 setPublicVersion()
将备用通知附加到普通通知中。
注意,用户始终可以最终控制其通知是否显示在锁定屏幕上,并且可以根据应用的通知渠道控制通知。
h.更新通知
如需在发出通知后对其进行更新,请再次调用 NotificationManagerCompat.notify()
,并向其传递您之前使用的相同 ID。如果之前的通知已关闭,系统会改为创建新通知。
您可以选择调用 setOnlyAlertOnce()
,这样您的通知只会在用户首次出现时通过声音、振动或视觉提示来打断用户,而不适用于后续更新。
注意:Android 会在更新通知时应用速率限制。如果您过于频繁地发布通知的更新(不到一秒内发布多项更新),系统可能会丢弃更新。
i.移除通知
除非发生以下情况之一,否则通知仍然可见:
- 用户关闭通知。
- 如果您在创建通知时调用了
setAutoCancel()
,用户便会点按该通知。 - 您可以针对特定通知 ID 调用
cancel()
。此方法还会删除持续性通知。 - 您调用了
cancelAll()
方法,该方法将移除之前发出的所有通知。 - 如果您在创建通知时使用
setTimeoutAfter()
设置了超时,则会经过指定的时长。如果需要,您可以在指定的超时时长过去之前取消通知。
j.有关即时通讯应用的最佳做法
为即时通讯和聊天应用创建通知时,请考虑此处列出的最佳实践。
使用 MessagingStyle
从 Android 7.0(API 级别 24)开始,Android 提供了专用于消息内容的通知样式模板。使用 NotificationCompat.MessagingStyle
类,您可以更改在通知中显示的多个标签,包括会话标题、其他消息和通知的内容视图。
val user = Person.Builder()
.setIcon(userIcon)
.setName(userName)
.build()
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("2 new messages with $sender")
.setContentText(subject)
.setSmallIcon(R.drawable.new_message)
.setStyle(NotificationCompat.MessagingStyle(user)
.addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getPerson())
.addMessage(messages[2].getText(), messages[2].getTime(), messages[2].getPerson())
)
.build()
从 Android 9.0(API 级别 28)开始,还需要使用 Person
类才能优化通知及其头像的呈现方式。
使用 NotificationCompat.MessagingStyle
时,请执行以下操作:
- 调用
MessagingStyle.setConversationTitle()
,为超过两个人的群聊设置标题。一个好的会话标题可以是群聊的名称,如果没有名称,可以是会话的参与者列表。否则,消息可能会被误认为属于与会话中最新消息的发送者的一对一对话。 - 使用
MessagingStyle.setData()
方法包含图片等媒体消息。支持模式 image/* 的 MIME 类型。
使用直接回复
直接回复可让用户以内嵌方式回复消息。
- 当用户使用内嵌回复操作回复后,请使用
MessagingStyle.addMessage()
更新MessagingStyle
通知,并且不要撤消或取消通知。不取消通知可让用户通过通知发送多次回复。 - 如需使内嵌回复操作与 Wear OS 兼容,请调用
Action.WearableExtender.setHintDisplayInlineAction(true)
。 - 通过向通知添加历史消息,使用
addHistoricMessage()
方法为直接回复对话提供上下文。
启用智能回复
- 如需启用智能回复,请对回复操作调用
setAllowGeneratedResponses(true)
。这样,当通知桥接到 Wear OS 设备时,用户就可以使用智能回复响应。智能回复响应由完全在手表上的机器学习模型使用NotificationCompat.MessagingStyle
通知提供的上下文生成,不会将数据上传到互联网来生成响应。
添加通知元数据
- 分配通知元数据,以告知系统当设备处于
Do Not Disturb mode
状态时如何处理应用通知。例如,使用addPerson()
或setCategory(Notification.CATEGORY_MESSAGE)
方法替换“勿扰”模式。
2、可展开样式的通知
基本通知通常包括标题、一行文本以及用户可以作为响应执行的操作。如需提供更多信息,您可以按照本文档中介绍的方式应用多个通知模板之一,创建可展开的大型通知。
首先,使用创建通知中所述的所有基本内容构建通知。然后,使用样式对象调用 setStyle()
,并提供与每个模板相对应的信息,如以下示例所示。
a.添加大图片
如需在通知中添加图片,请将 NotificationCompat.BigPictureStyle
的实例传递给 setStyle()
。
val notification = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.new_post)
.setContentTitle(imageTitle)
.setContentText(imageDescription)
.setStyle(NotificationCompat.BigPictureStyle()
.bigPicture(myBitmap))
.build()
若要使图片仅在通知收起时显示为缩略图(如下图所示),请调用 setLargeIcon()
并向其传递图片。然后,调用 BigPictureStyle.bigLargeIcon()
并向其传递 null
,这样大图标就会在通知展开时消失:
b.添加一大段文本
应用 NotificationCompat.BigTextStyle
以在通知的展开内容区域显示文本:
val notification = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.new_mail)
.setContentTitle(emailObject.getSenderName())
.setContentText(emailObject.getSubject())
.setLargeIcon(emailObject.getSenderAvatar())
//BigTextStyle .添加一大段文本
.setStyle(NotificationCompat.BigTextStyle()
.bigText(emailObject.getSubjectAndSnippet()))
.build()
提示: 如需为文本添加格式(例如粗体、斜体、换行符等),您可以使用 HTML 标记添加样式。
c.创建收件箱样式的通知
如果您想添加多个简短的摘要行(例如收到的电子邮件中的摘要),请对通知应用 NotificationCompat.InboxStyle
。这样,您就可以添加多段内容文本,并且每条文本都截断为一行,而不是由 NotificationCompat.BigTextStyle
提供的一行连续文本。
如需添加新行,最多可调用 addLine()
六次,如以下示例所示。如果添加的代码行超过 6 行,则仅显示前 6 行。
提示 :您可以使用 HTML 标记添加样式(例如加粗主题),以区分每行中的消息主题和消息内容。
val notification = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.baseline_email_24)
.setContentTitle("5 New mails from Frank")
.setContentText("Check them out")
.setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.logo))
.setStyle(
NotificationCompat.InboxStyle()
.addLine("Re: Planning")
.addLine("Delivery on its way")
.addLine("Follow-up")
)
.build()
创建一组通知
从 Android 7.0(API 级别 24)开始,您可以在一个组中显示相关通知。例如,如果您的应用针对收到的电子邮件显示通知,请将有关新电子邮件的所有通知放入同一个群组中,以便它们收起来。
如需支持较低版本,请添加摘要通知,该摘要通知将单独显示,以汇总所有单独的通知。通常,最好使用收件箱样式的通知来实现此目的。
注意 :通知组与通知渠道组不同。
满足以下所有条件,请使用通知组:
子通知是完整通知,可以单独显示,而无需通知组摘要。
-
单独显示子级通知有一个好处。例如:
它们是可操作的,具体操作特定于每条通知。
每条通知中都包含更多信息供用户查看。
如果您的通知不符合上述条件,不妨考虑使用新信息更新现有通知,或创建消息样式的通知,以便在同一对话中显示多项更新。
注意 :如果您的应用发出 4 条或更多条通知且未指定通知组,则系统会在 Android 7.0 及更高版本上将这些通知自动分组。
创建通知组并为其添加通知
如需创建通知组,请为该通知组定义一个唯一标识符字符串。然后,对于您想要添加到通知组中的每条通知,调用 setGroup()
并传入通知组名称。例如:
val GROUP_KEY_WORK_EMAIL = "com.android.example.WORK_EMAIL"
val newMessageNotification = NotificationCompat.Builder(this@MainActivity, CHANNEL_ID)
.setSmallIcon(R.drawable.new_mail)
.setContentTitle(emailObject.getSenderName())
.setContentText(emailObject.getSubject())
.setLargeIcon(emailObject.getSenderAvatar())
.setGroup(GROUP_KEY_WORK_EMAIL)
.build()
默认情况下,系统会根据通知的发布时间对其进行排序,但您可以通过调用 setSortKey()
更改通知顺序。
如果针对某个通知组的提醒必须由其他通知处理,请调用 setGroupAlertBehavior()
。例如,如果您只希望通知组的摘要发出通知,则该通知组中的所有子级都必须具有通知组提醒行为 GROUP_ALERT_SUMMARY
。其他选项包括 GROUP_ALERT_ALL
和 GROUP_ALERT_CHILDREN
。
设置通知组摘要
分组通知必须有一个额外的通知来充当通知组摘要。要启用分组通知,您必须设置通知组摘要。此通知组摘要必须包含通知组中其他通知的部分文本,以帮助用户了解通知组的内容。群组摘要的显示方式取决于 Android 版本:
在低于 7.0(API 级别 24)的 Android 版本中,由于无法显示嵌套通知组,系统仅显示您的通知组摘要通知,而隐藏所有其他通知。用户可以点按群组摘要通知以打开您的应用。
在 Android 7.0 及更高版本中,系统会将通知组摘要通知显示为一组嵌套通知,并用每个分组通知的文本摘要进行标记。不会显示您在群组摘要通知中设置的文本。用户可以展开嵌套的通知组以查看该组中的各个通知,如上图 所示。
即使较新版本的 Android 未显示您设计的通知组摘要文本,您也始终需要手动设置摘要才能启用分组通知。设备组摘要的行为在某些设备类型(例如穿戴式设备)上可能会有所不同。在群组摘要中设置富媒体内容有助于在所有设备和版本上提供最佳体验。
如需添加通知组摘要,请按以下步骤操作:
创建包含通知组说明的新通知 - 通常最好使用收件箱样式的通知来完成。
通过调用
setGroup()
将摘要通知添加到通知组中。通过调用
setGroupSummary(true)
指定必须将其用作通知组摘要。
// Use constant ID for notifications used as group summary.
val SUMMARY_ID = 0
val GROUP_KEY_WORK_EMAIL = "com.android.example.WORK_EMAIL"
val newMessageNotification1 = NotificationCompat.Builder(this@MainActivity, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notify_email_status)
.setContentTitle(emailObject1.getSummary())
.setContentText("You will not believe...")
.setGroup(GROUP_KEY_WORK_EMAIL)
.build()
val newMessageNotification2 = NotificationCompat.Builder(this@MainActivity, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notify_email_status)
.setContentTitle(emailObject2.getSummary())
.setContentText("Please join us to celebrate the...")
.setGroup(GROUP_KEY_WORK_EMAIL)
.build()
val summaryNotification = NotificationCompat.Builder(this@MainActivity, CHANNEL_ID)
.setContentTitle(emailObject.getSummary())
// Set content text to support devices running API level < 24.
.setContentText("Two new messages")
.setSmallIcon(R.drawable.ic_notify_summary_status)
// Build summary info into InboxStyle template.
.setStyle(NotificationCompat.InboxStyle()
.addLine("Alex Faarborg Check this out")
.addLine("Jeff Chang Launch Party")
.setBigContentTitle("2 new messages")
.setSummaryText("janedoe@example.com"))
// Specify which group this notification belongs to.
.setGroup(GROUP_KEY_WORK_EMAIL)
// Set this notification as the summary for the group.
.setGroupSummary(true)
.build()
NotificationManagerCompat.from(this).apply {
notify(emailNotificationId1, newMessageNotification1)
notify(emailNotificationId2, newMessageNotification2)
notify(SUMMARY_ID, summaryNotification)
}
摘要通知 ID 必须保持不变,以便仅发布一次,以便日后在摘要信息发生更改时进行更新。后续向该组添加内容会导致更新现有摘要。
自动分组
在 Android 7.0(API 级别 24)及更高版本中,如果您的应用发送了 4 条或更多通知,并且未指定组键或组摘要,系统可能会自动将这些通知分为一组。系统会自动分组显示通知,并附带通知组摘要通知,该摘要通知会标有某些分组通知的文本片段。与手动分组的通知一样,用户可以展开此摘要通知以查看各个通知。
自动分组行为在某些设备类型上可能会有所不同。为了在所有设备和版本上提供最佳体验,如果您知道通知必须分组,请指定组键和组摘要,以确保这些通知已分组。
d.在通知中显示对话
应用 NotificationCompat.MessagingStyle
可显示任意数量的人之间的依序消息。这非常适合即时通讯应用,因为它通过单独处理发送者名称和消息文本来为每条消息提供一致的布局,并且每条消息可以多行。
如需添加新消息,请调用 addMessage()
,并传递消息文本、接收时间和发送者姓名。您还可以将此信息作为 NotificationCompat.MessagingStyle.Message
对象传递
使用 NotificationCompat.MessagingStyle
时,为 setContentTitle()
和 setContentText()
指定的任何值都会被忽略。
您可以调用 setConversationTitle()
来添加显示在对话上方的标题。这可以是用户创建的群组名称;如果没有具体名称,也可以是对话中的参与者列表。请勿为一对一聊天设置对话标题,因为系统会利用此字段的存在来提示对话是一个群组。
此样式仅适用于搭载 Android 7.0(API 级别 24)及更高版本的设备。如前所述,使用兼容性库 (NotificationCompat
) 时,具有 MessagingStyle
的通知会自动回退为受支持的展开式通知样式。
为聊天对话创建此类通知时,请添加直接回复操作。
e.使用媒体控件创建通知
注意 :使用 AndroidX Media3 时,系统会自动创建媒体通知。
应用 MediaStyleNotificationHelper.MediaStyle
可显示媒体播放控件和曲目信息。
在构造函数中指定关联的 MediaSession
。这样,Android 就可以显示有关您媒体的正确信息。
调用 addAction()
最多五次,最多可以显示五个图标按钮。调用 setLargeIcon()
可设置专辑封面。
与其他通知样式不同,MediaStyle
还允许您通过指定三个操作按钮(这些按钮也会显示在收起视图中)来修改收起后尺寸的内容视图。为此,请向 setShowActionsInCompactView()
提供操作按钮索引。
val notification = NotificationCompat.Builder(context, CHANNEL_ID)
// Show controls on lock screen even when user hides sensitive content.
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setSmallIcon(R.drawable.ic_stat_player)
// Add media control buttons that invoke intents in your media service
.addAction(R.drawable.ic_prev, "Previous", prevPendingIntent) // #0
.addAction(R.drawable.ic_pause, "Pause", pausePendingIntent) // #1
.addAction(R.drawable.ic_next, "Next", nextPendingIntent) // #2
// Apply the media style template.
.setStyle(MediaStyleNotificationHelper.MediaStyle(mediaSession)
.setShowActionsInCompactView(1 /* #1: pause button \*/))
.setContentTitle("Wonderful music")
.setContentText("My Awesome Band")
.setLargeIcon(albumArtBitmap)
.build()
注意 :使用
MediaStyleNotificationHelper.MediaStyle
创建的通知的类别会设置为CATEGORY_TRANSPORT
,除非您使用setCategory()
设置其他类别。
f.其他资源
如需详细了解 MediaStyle
和展开式通知,请参阅以下参考文档。
- 对前台服务使用 MediaStyle 通知
- “People”示例应用
3、通话样式的通知
在 Android 12.0(API 级别 31)及更高版本中,系统提供 CallStyle
通知模板,以将来电通知与其他类型的通知区分开来。使用此模板可以创建来电或正在进行的通话通知。此模板支持大格式通知,其中包含来电信息和所需的操作(如接听或拒接来电)。
由于来电和正在进行的通话属于高优先级事件,因此这些通知在通知栏中会获得最高优先级。此排名还使系统能够将这些优先级较高的通话转接到其他设备。
CallStyle 通知模板包含以下必需操作:
- 接听或拒接来电。
- 挂断当前通话。
- 接听或挂断电话以过滤来电。
所需操作会以 intent 的形式传递,例如后面几部分中的 hangupIntent
和 answerIntent
。其中每个字段都是对系统维护的令牌的引用。令牌是一个轻量级对象,可以在不同应用和进程之间传递。系统负责管理令牌的生命周期,并确保即使创建 PendingIntent
的应用不再运行,它仍然可用。如果您为另一个应用提供 PendingIntent
,即表示您授予该应用执行指定操作的权限,如拒绝或接听。即使创建该 intent 的应用当前未运行,也会授予此权限。如需了解详情,请参阅 PendingIntent
的参考文档。
从 Android 14(API 级别 34)开始,您可以将来电通知配置为不可关闭。为此,请通过 Notification.Builder#setOngoing(true)
使用 Notification.FLAG_ONGOING_EVENT
的 CallStyle
通知。
// Create a new call, setting the user as the caller.
val incomingCaller = Person.Builder()
.setName("Jane Doe")
.setImportant(true)
.build()
来电
使用 forIncomingCall() 方法为来电创建通话样式通知。
// Create a call style notification for an incoming call.
val builder = Notification.Builder(context, CHANNEL_ID)
.setContentIntent(contentIntent)
.setSmallIcon(smallIcon)
.setStyle(
Notification.CallStyle.forIncomingCall(caller, declineIntent, answerIntent))
.addPerson(incomingCaller)
当前通话
使用 forOngoingCall() 方法为正在进行的通话创建通话样式通知。
// Create a call style notification for an ongoing call.
val builder = Notification.Builder(context, CHANNEL_ID)
.setContentIntent(contentIntent)
.setSmallIcon(smallIcon)
.setStyle(
Notification.CallStyle.forOngoingCall(caller, hangupIntent))
.addPerson(second_caller)
过滤来电
使用 forScreeningCall() 方法可创建用于过滤来电的调用样式通知。
// Create a call style notification for screening a call.
val builder = Notification.Builder(context, CHANNEL_ID)
.setContentIntent(contentIntent)
.setSmallIcon(smallIcon)
.setStyle(
Notification.CallStyle.forScreeningCall(caller, hangupIntent, answerIntent))
.addPerson(second_caller)
实现与更多 Android 版本的兼容性
将 API 版本 30 或更低版本的 CallStyle
通知与前台服务相关联,以便为它们分配 API 级别 31 或更高级别中所指定的最高排名。此外,API 版本 30 或更低版本上的 CallStyle
通知可以使用 setColorized()
方法将通知标记为彩色,从而达到类似排名。
将 Telecom API 与 CallStyle
通知搭配使用。如需了解详情,请参阅 Telecom 框架概览。
4、具有时效性的通知
在某些情况下,您的应用可能需要紧急吸引用户的注意力,例如闹钟正在响铃或有来电时。在以搭载 Android 9(API 级别 28)或更低版本的设备为目标平台的应用中,您可以通过在应用在后台运行时启动 activity 来处理此问题。本文档介绍了如何在搭载 Android 10(API 级别 29)到 Android 13(API 级别 33)的设备上实现此行为。
添加 POST_NOTIFICATIONS 权限
从 Android 13 开始,将以下行添加到 AndroidManifest.xml 文件中:
<manifest ...>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<application ...>
...
</application>
</manifest>
创建通知渠道
创建通知渠道以正确显示通知,并让用户在应用设置中管理通知。如需详细了解通知渠道,请参阅创建和管理通知渠道。
在 Application
类的 onCreate
方法中创建通知渠道:
class DACapp : Application() {
override fun onCreate() {
super.onCreate()
val channel = NotificationChannel(
CHANNEL_ID,
"High priority notifications",
NotificationManager.IMPORTANCE_HIGH
)
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
当用户首次运行您的应用时,他们会在应用的应用信息系统屏幕中看到如图 1 所示的内容:
管理通知权限
从 Android 13 开始,请求通知权限后才能向用户显示通知。
val permissionLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestPermission(),
onResult = { hasNotificationPermission = it }
)
...
Button(
onClick = {
if (!hasNotificationPermission) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}
}
},
) {
Text(text = "Request permission")
}
创建高优先级通知
创建通知时,请添加描述性标题和消息。
private fun showNotification() {
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val notificationBuilder =
NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.baseline_auto_awesome_24)
.setContentTitle("HIGH PRIORITY")
.setContentText("Check this dog puppy video NOW!")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_RECOMMENDATION)
notificationManager.notify(666, notificationBuilder.build())
}
向用户显示通知
Button(onClick = { showNotification() }) {
Text(text = "Show notification")
}
持续性通知
当您向用户显示通知时,他们可以确认或关闭应用的提醒。例如,用户可以接听或拒绝来电。
注意 :当用户使用设备时,系统界面可能会显示浮动通知,而不是启动全屏 intent。
如果您的通知正在进行(例如来电),请将该通知与前台服务相关联。以下代码段展示了如何显示与前台服务关联的通知:
// Provide a unique integer for the "notificationId" of each notification.
startForeground(notificationId, notification)
5、自定义样式通知
为了使通知在不同的 Android 版本上呈现最佳效果,请使用标准通知模板来构建通知。如果您想在通知中提供更多内容,不妨考虑使用某个可展开的通知模板。
不过,如果系统模板无法满足您的需求,您可以使用自己的通知布局。
注意 :使用自定义通知布局时,请特别注意确保您的自定义布局适用于不同的设备屏幕方向和分辨率。虽然此建议适用于所有界面布局,但对于通知尤为重要,因为抽屉式通知栏中的空间有限。自定义通知布局的可用高度取决于 Android 版本。在某些版本中,收起视图布局可低至 48 dp,浮动视图布局可低至 88 dp,展开视图布局可低至 252 dp。
为内容区域创建自定义布局
如果您需要自定义布局,可以将 NotificationCompat.DecoratedCustomViewStyle
应用于通知。借助此 API,您可以为通常由标题和文本内容占据的内容区域提供自定义布局,同时仍对通知图标、时间戳、子文本和操作按钮使用系统装饰。
此 API 的工作方式与可展开的通知模板类似,都是基于基本通知布局构建,如下所示:
- 使用
NotificationCompat.Builder
构建基本通知。 - 调用
setStyle()
,并向其传递NotificationCompat.DecoratedCustomViewStyle
的实例。 - 将自定义布局膨胀为
RemoteViews
的实例。 - 调用
setCustomContentView()
以设置收起后通知的布局。 - 您还可以选择调用
setCustomBigContentView()
,为展开式通知设置不同的布局。
注意 :如果您要为媒体播放控件创建自定义通知,请遵循相同的建议,但请改用 NotificationCompat.DecoratedMediaCustomViewStyle 类。
准备布局
需要使用 small 和 large 布局。在此示例中,small 布局可能如下所示:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/notification_title"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:text="Small notification, showing only a title" />
</LinearLayout>
large 布局可能如下所示:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="300dp"
android:orientation="vertical">
<TextView
android:id="@+id/notification_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Large notification, showing a title and a body." />
<TextView
android:id="@+id/notification_body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="This is the body. The height is manually forced to 300dp." />
</LinearLayout>
构建并显示通知
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// Get the layouts to use in the custom notification.
val notificationLayout = RemoteViews(packageName, R.layout.notification_small)
val notificationLayoutExpanded = RemoteViews(packageName, R.layout.notification_large)
// Apply the layouts to the notification.
val customNotification = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.notification_icon)
.setStyle(NotificationCompat.DecoratedCustomViewStyle())
.setCustomContentView(notificationLayout)
.setCustomBigContentView(notificationLayoutExpanded)
.build()
notificationManager.notify(666, customNotification)
注意,通知的背景颜色可能因设备和版本而异。在自定义布局中,对文本应用 TextAppearance_Compat_Notification 样式,对标题应用 TextAppearance_Compat_Notification_Title 等支持库样式,如以下示例所示。这些样式会根据颜色变化进行调整,因此您最终不会出现黑底白字或白底文字的情况。
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="@string/notification_title"
android:id="@+id/notification_title"
/>
避免在 RemoteViews 对象上设置背景图片,否则文字可能会无法读取。
通知超时时间结束后,通知仅会显示在系统栏中
创建完全自定义的通知布局
注意 :以 Android 12(API 级别 31)或更高版本为目标平台的应用无法创建完全自定义的通知。相反,系统会应用与
Notification.DecoratedCustomViewStyle
的行为几乎完全相同的标准模板。
如果您不想使用标准通知图标和标题装饰通知,请按照上述步骤操作,但不要调用 setStyle()。
注意: 我们不建议使用未经修饰的通知,因为这种通知与其他通知不匹配,并且在对通知区域应用了不同样式的设备上可能导致严重的布局兼容性问题。