15、通知的快捷回复:RemoteInput的构建、InlineReply、回复后的UI更新、历史回复记录

各位同学,今天我们来聊聊通知里一个非常实用的功能——快捷回复。说白了,就是用户不用打开你的App,直接在通知栏里就能回复消息。这个功能在IM类应用里特别常见,比如微信、QQ的消息通知,你下拉通知栏就能直接打字回复。

我个人觉得,这个功能是提升用户体验的利器。你想啊,用户正在刷抖音,突然来了一条消息,如果还要切到你的App才能回复,那体验就断档了。直接在通知栏里回一句“好的”,然后继续刷抖音,多顺畅。

15.1 RemoteInput 的构建

要实现快捷回复,核心就是 RemoteInput 这个类。它负责定义用户输入的内容,以及如何把输入结果传回你的App。

构建一个 RemoteInput 其实很简单,就像搭积木一样。我习惯用 Builder 模式来创建:

// 1. 创建一个 RemoteInput 对象
val remoteInput = RemoteInput.Builder("key_reply_content")
    .setLabel("输入回复内容") // 输入框的占位提示
    .setChoices(arrayOf("好的", "马上到", "收到")) // 预设的快捷回复选项
    .build()

// 2. 把这个 RemoteInput 绑定到 Action 上
val replyAction = NotificationCompat.Action.Builder(
    R.drawable.ic_reply, // 回复按钮的图标
    "回复",              // 按钮文字
    replyPendingIntent   // 点击后触发的 PendingIntent
)
.addRemoteInput(remoteInput) // 关键:把 RemoteInput 加进去
.build()

// 3. 把 Action 添加到通知上
val notification = NotificationCompat.Builder(context, CHANNEL_ID)
    .setContentTitle("新消息")
    .setContentText("你有一条未读消息")
    .addAction(replyAction) // 添加回复按钮
    .build()

这里要注意,RemoteInput.Builder 的构造函数里需要一个 resultKey,这个 key 很重要。它就像一把钥匙,后面你从 Intent 里取用户输入的内容时,就要用这个 key 来取。

小提示:我个人习惯把 resultKey 定义成常量,比如 const val KEY_REPLY = "key_reply_content"。这样后面取数据时不容易写错。

15.2 InlineReply 的实现

嗯,这里要提一下 InlineReply。其实它不是一个单独的类,而是 Android 7.0(API 24)引入的一种快捷回复样式。说白了,就是让用户可以直接在通知栏里输入文字,而不是弹出一个对话框。

要实现 InlineReply,你需要在构建通知时,把 RemoteInput 添加到 Action 上,并且设置 setAllowGeneratedReplies(true)。这样系统就会自动帮你生成一个内联输入框。

// 启用内联回复
val replyAction = NotificationCompat.Action.Builder(
    R.drawable.ic_reply,
    "回复",
    replyPendingIntent
)
.addRemoteInput(remoteInput)
.setAllowGeneratedReplies(true) // 允许系统生成内联回复
.build()

我在项目中遇到过一个问题:有些国产手机对 InlineReply 的支持不太好,会显示成弹窗。嗯,这个没办法,毕竟是厂商定制的问题。但大部分主流机型都支持得很好。

15.3 回复后的UI更新

用户回复之后,通知栏里的内容应该立即更新,让用户看到“已回复”的状态。否则用户会困惑:我到底回复成功了没有?

这里的关键是:在 PendingIntent 触发的 BroadcastReceiverService 里,获取用户输入的内容,然后更新通知。

// 在 BroadcastReceiver 中处理回复
class ReplyReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        // 从 Intent 中提取 RemoteInput 的结果
        val results = RemoteInput.getResultsFromIntent(intent)
        val replyText = results?.getCharSequence("key_reply_content") ?: return

        // 更新通知,显示回复内容
        val updatedNotification = NotificationCompat.Builder(context, CHANNEL_ID)
            .setContentTitle("你已回复")
            .setContentText("回复: $replyText")
            .setSmallIcon(R.drawable.ic_message)
            .build()

        NotificationManagerCompat.from(context).notify(NOTIFICATION_ID, updatedNotification)
    }
}

你想想看,如果不更新通知,用户下拉通知栏看到的还是原来的消息内容,那多尴尬。我建议在回复成功后,把通知的内容改成“你已回复:xxx”,这样用户一目了然。

注意:更新通知时,一定要使用相同的 NOTIFICATION_ID。否则系统会认为你创建了一个新通知,而不是更新旧通知。

15.4 历史回复记录

这个功能可能很多人会忽略,但其实挺重要的。用户可能想看看自己之前回复了什么内容。比如在群聊里,用户回复了“收到”,过一会儿忘了,想翻看一下。

实现历史回复记录,我一般有两种做法:

  1. 本地存储:把回复内容存到 SharedPreferences 或数据库里。每次回复时,把内容追加到历史记录中。
  2. 通知扩展:利用 NotificationCompat.MessagingStyle 来展示历史消息。这个样式专门用来展示聊天记录,支持多条消息的展示。

我个人更推荐第二种方式,因为它更符合通知的语义。来看代码:

// 使用 MessagingStyle 展示历史回复
val messagingStyle = NotificationCompat.MessagingStyle("我")
    .setConversationTitle("群聊")

// 添加历史消息
messagingStyle.addMessage("你好", System.currentTimeMillis() - 60000, "张三")
messagingStyle.addMessage("在吗?", System.currentTimeMillis() - 30000, "张三")
messagingStyle.addMessage("好的,收到", System.currentTimeMillis(), null) // null 表示是自己发的

val notification = NotificationCompat.Builder(context, CHANNEL_ID)
    .setStyle(messagingStyle)
    .addAction(replyAction)
    .build()

这样,用户下拉通知栏就能看到完整的聊天记录,包括自己回复的内容。我曾经在一个项目里用这种方式实现了类似微信的聊天通知效果,用户反馈非常好。

知识体系图

下面这张图总结了本章的核心知识点和流程:

通知快捷回复知识体系 RemoteInput 构建 InlineReply 内联回复 回复后 UI 更新 历史回复记录 setAllowGeneratedReplies(true) RemoteInput.getResultsFromIntent() MessagingStyle 展示历史 提升用户体验:无需打开App即可回复

避坑指南

最后,分享几个我踩过的坑:

  • PendingIntent 的 Flag:我曾经因为用了 FLAG_UPDATE_CURRENT 导致回复内容一直取不到。后来发现要用 FLAG_MUTABLE(Android 12+ 必须),否则系统会拒绝传递 RemoteInput 的数据。
  • 通知渠道的重要性:如果通知渠道没有设置 setAllowBubbles(true),有些机型可能不会显示内联回复。我建议在创建渠道时就把这个开关打开。
  • 历史记录的长度:MessagingStyle 虽然好用,但不要添加太多历史消息。通知栏空间有限,我一般只保留最近3-5条记录。

好了,关于通知的快捷回复,今天就讲到这里。记住,核心就是 RemoteInput 的构建、InlineReply 的启用、回复后的 UI 更新,以及历史记录的展示。把这四点吃透,你就能做出体验很好的快捷回复功能了。


公众号:蓝海资料掘金营,微信deep3321