第4章 通知样式扩展:BigTextStyle、BigPictureStyle、InboxStyle、MediaStyle、MessagingStyle

4.1 通知样式扩展原理

基础通知仅能显示一行标题+一行内容,在通知栏折叠状态下空间极其有限。Material Design 通过 NotificationCompat.Style 抽象类提供了 5 种展开样式,当用户下拉通知或系统进入展开区域时,样式会接管内容区域的渲染,呈现远多于基础通知的信息量。

所有样式均通过 setStyle() 注入到 NotificationCompat.Builder,样式对象本身只负责构造展开后的内容模型,最终渲染由系统 NotificationService 完成。因此样式代码只在构建端存在,不会增加 APK 体积,但需注意样式数据序列化后通过 Binder 传递的大小限制(通常 < 512KB)。

核心原则:样式仅改变通知的 展开态 布局,折叠态依然由 setContentTitle() 和 setContentText() 决定。务必保证折叠态内容完整,否则用户可能看不到任何信息。
通知样式扩展体系 NotificationCompat BigTextStyle bigText() 适合长文本通知 BigPictureStyle bigPicture() 适合图片展示通知 InboxStyle addLine() 适合邮件摘要通知 MediaStyle setMediaSession() 适合媒体播放控制 MessagingStyle addMessage() 适合即时消息通知 所有样式均通过 setStyle() 注入,只影响通知展开态布局 1 2 3 4 5

4.2 BigTextStyle — 长文本展开式

当通知需要展示超出单行限制的文本内容时,BigTextStyle 可在展开态呈现最多 256 个可见字符(实际限制取决于系统渲染区域)。

实现流程

  1. 创建 NotificationCompat.Builder,设置折叠态标题与内容
  2. 实例化 NotificationCompat.BigTextStyle
  3. 调用 bigText(CharSequence) 设置展开后完整文本
  4. 通过 setStyle() 注入 Builder
  5. 调用 notify() 发布通知
val builder = NotificationCompat.Builder(context, CHANNEL_ID_TXT)
    .setSmallIcon(R.drawable.ic_article)
    .setContentTitle("技术周刊")
    .setContentText("Android 15 新特性一览")
    .setStyle(
        NotificationCompat.BigTextStyle()
            .bigText("Android 15 引入了多项开发者关注的新特性,包括" +
                     "更好的折叠屏适配、更高效的媒体管道、全新通知" +
                     "冷却机制以及隐私沙盒的进一步升级。本文详细" +
                     "解读每个变更对现有应用的影响……")
    )
    .setPriority(NotificationCompat.PRIORITY_DEFAULT)

NotificationManagerCompat.from(context).notify(1001, builder.build())
注意:bigText() 接收 Spanned 对象,支持富文本(如粗体、链接)。但不要在长文本中嵌入过多图片或复杂 span,否则可能导致 RemoteViews 渲染超时(ANR)。

4.3 BigPictureStyle — 大图展示式

适用于社交媒体、摄影应用或任何需要突出视觉内容的通知。展开态会将 Bitmap 渲染为宽幅大图,同时支持设置大图标作为缩略图。

关键API

方法参数说明
bigPicture(Bitmap)展开主图宽度建议 720px+,高度自适应,过大将被系统裁剪
bigLargeIcon(Bitmap?)右下角图标传 null 可隐藏图标;默认复用 setLargeIcon()
setSummaryText(String)底部摘要在大图下方显示一行说明文字
val largeBitmap = BitmapFactory.decodeResource(resources, R.drawable.landscape_photo)
// 避免过大图片:建议做一次采样
val options = BitmapFactory.Options().apply { inSampleSize = 2 }
val sampled = BitmapFactory.decodeResource(resources, R.drawable.landscape_photo, options)

val builder = NotificationCompat.Builder(context, CHANNEL_ID_IMG)
    .setSmallIcon(R.drawable.ic_image)
    .setContentTitle("精彩瞬间")
    .setContentText("点击查看大图")
    .setStyle(
        NotificationCompat.BigPictureStyle()
            .bigPicture(sampled)
            .bigLargeIcon(null)          // 隐藏大图标,突出主图
            .setSummaryText("来自摄影大赛投稿")
    )
    .setPriority(NotificationCompat.PRIORITY_LOW)

NotificationManagerCompat.from(context).notify(1002, builder.build())
性能要点:大图 Bitmap 在传递前务必做尺寸压缩。建议宽度不超过 1080px,文件大小控制在 300KB 以内。使用 BitmapFactory.Options.inSampleSize 或 Glide 预缩略。

4.4 InboxStyle — 收件箱摘要式

专为邮件、聊天聚合、系统日志等需要展示多条摘要的场景设计。展开态以 行列表 呈现,每行独立,支持最多 5~7 行(视系统 UI 而定)。

val style = NotificationCompat.InboxStyle()
    .addLine("📧 设计评审会议 10:00")
    .addLine("📧 代码审查反馈 #342")
    .addLine("📧 项目周报截止提醒")
    .addLine("📧 新版本提测通知")
    .setSummaryText("共 4 封未读邮件")

val builder = NotificationCompat.Builder(context, CHANNEL_ID_INBOX)
    .setSmallIcon(R.drawable.ic_email)
    .setContentTitle("企业邮箱")
    .setContentText("4 封新邮件")
    .setStyle(style)
    .setNumber(4)   // 右上角角标
    .setPriority(NotificationCompat.PRIORITY_HIGH)

NotificationManagerCompat.from(context).notify(1003, builder.build())
最佳实践:addLine() 的文本建议控制在一行内,超长会被截断。使用 setSummaryText() 提供总览信息,该文字在 Android 7.0+ 上会显示在列表底部。

4.5 MediaStyle — 媒体控制式

用于音乐、播客、视频播放等场景,展开态显示 播放控制按钮 和 进度条(部分系统)。必须配合 MediaSessionCompat 使用才能正确同步播放状态。

依赖与初始化

// build.gradle (app)
implementation "androidx.media:media:1.7.0"   // 提供 MediaStyle 与 MediaSessionCompat

完整实现

// 1. 创建 MediaSessionCompat
val mediaSession = MediaSessionCompat(context, "PlayerSession")
mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or
                       MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS)
mediaSession.setActive(true)

// 2. 构建媒体通知
val style = androidx.media.app.NotificationCompat.MediaStyle()
    .setMediaSession(mediaSession.sessionToken)
    .setShowActionsInCompactView(0, 2)   // 折叠态显示 播放 和 下一首

val playAction = NotificationCompat.Action.Builder(
    R.drawable.ic_pause, "暂停", pausePendingIntent
).build()
val nextAction = NotificationCompat.Action.Builder(
    R.drawable.ic_skip_next, "下一首", nextPendingIntent
).build()

val builder = NotificationCompat.Builder(context, CHANNEL_ID_MEDIA)
    .setSmallIcon(R.drawable.ic_music_note)
    .setContentTitle("正在播放")
    .setContentText("Song Title — Artist")
    .setStyle(style)
    .addAction(playAction)
    .addAction(nextAction)
    .setOngoing(true)                       // 不可滑动清除
    .setPriority(NotificationCompat.PRIORITY_MAX)

NotificationManagerCompat.from(context).notify(1004, builder.build())
重要:Android 13+ 要求动态申请 POST_NOTIFICATIONS 权限,媒体通知同样受此约束。同时,MediaStyle 在部分国产 ROM 上可能无法显示标准控制栏,建议使用 setCustomBigContentView() 作为降级方案。

4.6 MessagingStyle — 消息对话式

专为即时通讯应用设计,展开态以 气泡对话 形式展示多条消息,支持发送方头像、时间戳、消息状态(已读/未读)。Android 11+ 可用此样式直接创建 对话通知(Conversations) 并支持 Bubble 浮窗。

Person 对象构建

val me = Person.Builder()
    .setName("我")
    .setIcon(IconCompat.createWithResource(context, R.drawable.avatar_me))
    .setKey("user_self")
    .build()

val alice = Person.Builder()
    .setName("Alice")
    .setIcon(IconCompat.createWithResource(context, R.drawable.avatar_alice))
    .setKey("user_alice")
    .setImportant(true)   // Android 11+ 标记为重要联系人
    .build()

构建消息通知

val style = NotificationCompat.MessagingStyle(me)
    .setConversationTitle("项目讨论组")          // 群聊时必填
    .addMessage("下午三点开会,请准时参加", 
                System.currentTimeMillis() - 300_000, alice)
    .addMessage("收到,我准备材料", 
                System.currentTimeMillis() - 240_000, me)
    .addMessage("文档已上传到共享目录", 
                System.currentTimeMillis() - 120_000, alice)
    .setGroupConversation(true)                 // 标记为群聊

val builder = NotificationCompat.Builder(context, CHANNEL_ID_MSG)
    .setSmallIcon(R.drawable.ic_chat)
    .setContentTitle("项目讨论组")
    .setContentText("Alice: 下午三点开会")
    .setStyle(style)
    .setShortcutId("conversation_project")      // Android 11+ 对话标识
    .setPriority(NotificationCompat.PRIORITY_HIGH)

// Android 13+ 需运行时权限
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU ||
    ContextCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) ==
    PackageManager.PERMISSION_GRANTED) {
    NotificationManagerCompat.from(context).notify(1005, builder.build())
}
对话通知进阶:在 Android 11+ 设备上,使用 setShortcutId() 关联 ShortcutInfo 可自动启用 Bubble 浮窗。用户长按通知即可弹出悬浮聊天窗口,大幅提升 IM 应用的消息回复效率。

4.7 样式对比与选型指南

样式核心方法最佳场景内容容量特殊依赖
BigTextStylebigText()长文章、日志、更新说明≈ 256 字符无
BigPictureStylebigPicture()摄影、社交、商品展示1 张大图Bitmap 尺寸控制
InboxStyleaddLine()邮件聚合、系统告警列表5~7 行无
MediaStylesetMediaSession()音乐、播客、视频播放3~5 个 ActionMediaSessionCompat + 前台服务
MessagingStyleaddMessage()即时通讯、聊天对话10+ 条消息Person 对象 + ShortcutId(可选)

4.8 调试与常见错误

调试方法

  • adb 通知转储:adb shell dumpsys notification --noredact 查看所有通知的完整样式数据、是否被拦截、通道评级。
  • 通知展开预览:Android Studio 的 Notification Inspector(Tools > Notification Inspector)可实时捕获设备通知并预览折叠/展开态。
  • 测试各 API 级别:不同厂商 ROM 对展开样式的渲染存在差异,建议在 Google Pixel、Samsung、Xiaomi 等主流设备上验证。

常见错误与解决方案

错误现象根本原因解决方案
通知完全不显示未调用 setSmallIcon() 或通道未创建确保 Builder 设置了 smallIcon,且 NotificationChannel 已注册
BigPictureStyle 崩溃:
RemoteException: BadParcelable
Bitmap 过大导致 Binder 事务超出 512KB对 Bitmap 采样压缩,或使用 URI 加载并设置 bigPicture() 时传 scaled bitmap
MediaStyle 控制按钮不响应PendingIntent 未正确关联 MediaSessionCompat.Callback验证 PendingIntent 的 Intent 指向的 Service/BroadcastReceiver 已实现相应回调
MessagingStyle 不显示头像Person.setIcon() 使用了普通 Bitmap 而非 IconCompat统一使用 IconCompat.createWithResource() 或 IconCompat.createWithURI()
Android 13+ 通知不弹出未动态请求 POST_NOTIFICATIONS 权限在代码中申请 Manifest.permission.POST_NOTIFICATIONS,并在 onRequestPermissionsResult 中处理授权
InboxStyle 行数超过限制被截断系统 UI 单屏最多展示 7 行(含摘要)控制 addLine() 在 5 行以内,多余信息用 setSummaryText() 概括
稳定性建议:所有样式在 setStyle() 之前都应捕获可能的异常(如 OOM、BadParcelable),建议使用 try-catch 包裹样式构造逻辑,并在 catch 中降级为基础通知,确保用户至少能看到折叠态内容。