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 来取。
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 触发的 BroadcastReceiver 或 Service 里,获取用户输入的内容,然后更新通知。
// 在 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 历史回复记录
这个功能可能很多人会忽略,但其实挺重要的。用户可能想看看自己之前回复了什么内容。比如在群聊里,用户回复了“收到”,过一会儿忘了,想翻看一下。
实现历史回复记录,我一般有两种做法:
- 本地存储:把回复内容存到
SharedPreferences或数据库里。每次回复时,把内容追加到历史记录中。 - 通知扩展:利用
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()
这样,用户下拉通知栏就能看到完整的聊天记录,包括自己回复的内容。我曾经在一个项目里用这种方式实现了类似微信的聊天通知效果,用户反馈非常好。
知识体系图
下面这张图总结了本章的核心知识点和流程:
避坑指南
最后,分享几个我踩过的坑:
- PendingIntent 的 Flag:我曾经因为用了
FLAG_UPDATE_CURRENT导致回复内容一直取不到。后来发现要用FLAG_MUTABLE(Android 12+ 必须),否则系统会拒绝传递 RemoteInput 的数据。 - 通知渠道的重要性:如果通知渠道没有设置
setAllowBubbles(true),有些机型可能不会显示内联回复。我建议在创建渠道时就把这个开关打开。 - 历史记录的长度:MessagingStyle 虽然好用,但不要添加太多历史消息。通知栏空间有限,我一般只保留最近3-5条记录。
好了,关于通知的快捷回复,今天就讲到这里。记住,核心就是 RemoteInput 的构建、InlineReply 的启用、回复后的 UI 更新,以及历史记录的展示。把这四点吃透,你就能做出体验很好的快捷回复功能了。
公众号:蓝海资料掘金营,微信deep3321