6、通知Action与交互:添加Action按钮、内联回复(RemoteInput)、进度条通知、媒体风格通知。

通知如果只是展示信息,那它就是个「哑巴」。真正让通知活起来的,是交互。用户不用打开App,直接在通知栏就能完成操作——这才是Android通知设计的精髓。

我个人习惯把通知交互分成四个层次:点击跳转(最基础)、Action按钮(快速操作)、内联回复(输入交互)、进度/媒体(实时反馈)。今天咱们就把后面三个讲透。

核心知识点一览

  • Action按钮:最多3个,用PendingIntent绑定操作
  • RemoteInput:在通知栏直接输入文字回复
  • 进度条通知:确定进度 vs 不确定进度
  • 媒体风格通知:MediaStyle + 播放控制
通知交互体系 Notification Action Action按钮 最多3个,PendingIntent 内联回复 RemoteInput + Broadcast 进度条通知 setProgress() 更新 媒体风格 MediaStyle + 播放控制 所有交互都依赖 PendingIntent 或 Service 绑定 Android 12+ 需要动态申请通知权限

6.1 Action按钮:让通知不止于「看」

Action按钮说白了就是在通知底部加几个可点击的按钮。比如微信消息的「回复」和「标记已读」,就是典型的Action。

实现起来其实不复杂,核心就三步:

  1. 创建PendingIntent(指向你要执行的操作)
  2. 调用addAction()方法
  3. 在BroadcastReceiver或Service里处理回调
// 创建Action按钮
Intent acceptIntent = new Intent(this, ActionReceiver.class);
acceptIntent.setAction("ACTION_ACCEPT");
PendingIntent acceptPendingIntent = PendingIntent.getBroadcast(
    this, 0, acceptIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);

NotificationCompat.Action action = new NotificationCompat.Action.Builder(
    R.drawable.ic_check, "接受", acceptPendingIntent
).build();

// 构建通知
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
    .setSmallIcon(R.drawable.ic_notification)
    .setContentTitle("好友请求")
    .setContentText("张三请求添加你为好友")
    .addAction(action)  // 添加Action
    .setAutoCancel(true);

NotificationManagerCompat.from(this).notify(1001, builder.build());

💡 我的经验

Action按钮最多只能加3个,超过会被系统忽略。我有个项目曾经加了4个,结果最后一个死活不显示,查了半天文档才发现这个限制。另外,Android 12开始,PendingIntent必须指定FLAG_IMMUTABLE或FLAG_MUTABLE,否则会抛异常。

6.2 内联回复:在通知栏直接打字

你想想看,收到一条消息,下拉通知栏直接打字回复,不用打开App——这个体验多流畅。这就是RemoteInput的功劳。

RemoteInput是Android Wear(手表)时代引入的,后来在手机上也普及了。它的工作流程是这样的:

  • 在Action里嵌入一个RemoteInput对象
  • 用户点击回复按钮后,弹出输入框
  • 输入内容通过BroadcastReceiver回传给App
// 1. 定义RemoteInput的Key
private static final String KEY_REPLY = "key_reply";

// 2. 创建RemoteInput
RemoteInput remoteInput = new RemoteInput.Builder(KEY_REPLY)
    .setLabel("输入回复内容")
    .build();

// 3. 创建回复Action
Intent replyIntent = new Intent(this, ReplyReceiver.class);
replyIntent.setAction("ACTION_REPLY");
PendingIntent replyPendingIntent = PendingIntent.getBroadcast(
    this, 1, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);

NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(
    R.drawable.ic_reply, "回复", replyPendingIntent
)
.addRemoteInput(remoteInput)  // 关键:绑定RemoteInput
.build();

// 4. 构建通知
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
    .setSmallIcon(R.drawable.ic_message)
    .setContentTitle("新消息")
    .setContentText("你好,在吗?")
    .addAction(replyAction);

NotificationManagerCompat.from(this).notify(1002, builder.build());

然后在BroadcastReceiver里取出用户输入:

public class ReplyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 从RemoteInput中提取回复内容
        Bundle results = RemoteInput.getResultsFromIntent(intent);
        if (results != null) {
            CharSequence replyText = results.getCharSequence(KEY_REPLY);
            // 处理回复逻辑...
            Toast.makeText(context, "回复: " + replyText, Toast.LENGTH_SHORT).show();
        }
    }
}

⚠️ 避坑指南

我曾经踩过一个坑:RemoteInput的Key必须和Builder时传入的Key完全一致,大小写都不能错。另外,如果用户没有输入直接发送,getCharSequence返回的是空字符串,不是null,记得判空处理。

6.3 进度条通知:给用户一个「进度感」

下载文件、上传图片、处理数据……这些耗时操作如果没有任何反馈,用户会以为App卡死了。进度条通知就是用来解决这个问题的。

Android支持两种进度条:

类型 方法 适用场景
确定进度 setProgress(max, progress, false) 下载文件、批量处理(知道总量)
不确定进度 setProgress(0, 0, true) 连接服务器、解压文件(不知道总量)
// 确定进度示例:文件下载
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
    .setSmallIcon(R.drawable.ic_download)
    .setContentTitle("正在下载")
    .setContentText("下载中...")
    .setOngoing(true);  // 不可滑动清除

// 模拟下载进度更新
new Thread(() -> {
    for (int i = 0; i <= 100; i += 10) {
        builder.setProgress(100, i, false);
        builder.setContentText("已下载 " + i + "%");
        NotificationManagerCompat.from(this).notify(1003, builder.build());
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    // 下载完成
    builder.setContentText("下载完成")
           .setProgress(0, 0, false)  // 移除进度条
           .setOngoing(false);
    NotificationManagerCompat.from(this).notify(1003, builder.build());
}).start();

💡 我的习惯

不确定进度时,我一般会配合一个「正在加载」的动画图标,让用户知道程序还在跑。另外,进度更新不要太频繁,每200ms更新一次就够,否则会浪费CPU和电量。

6.4 媒体风格通知:播放控制一步到位

音乐播放器、播客App、视频App……这些场景下,用户希望在下拉通知栏就能控制播放/暂停、切歌。MediaStyle就是为此设计的。

MediaStyle和普通通知的区别在于:

  • 使用MediaStyle装饰器,让通知样式更紧凑
  • 可以显示专辑封面(大图标)
  • 支持最多5个Action按钮(普通通知只能3个)
  • 配合MediaSession实现更完善的媒体控制
// 创建媒体通知
NotificationCompat.MediaStyle mediaStyle = new NotificationCompat.MediaStyle()
    .setMediaSession(mediaSession.getSessionToken())  // 绑定MediaSession
    .setShowActionsInCompactView(0, 1, 2);  // 在紧凑模式下显示哪些按钮

// 创建播放控制Action
Intent playIntent = new Intent(this, MediaControlReceiver.class);
playIntent.setAction("ACTION_PLAY_PAUSE");
PendingIntent playPendingIntent = PendingIntent.getBroadcast(
    this, 0, playIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);

NotificationCompat.Action playAction = new NotificationCompat.Action.Builder(
    R.drawable.ic_pause, "暂停", playPendingIntent
).build();

// 上一首、下一首类似...

NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
    .setSmallIcon(R.drawable.ic_music_note)
    .setContentTitle("歌曲名称")
    .setContentText("歌手名")
    .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.album_art))
    .setStyle(mediaStyle)  // 应用MediaStyle
    .addAction(prevAction)
    .addAction(playAction)
    .addAction(nextAction)
    .setOngoing(true);  // 播放中不可清除

NotificationManagerCompat.from(this).notify(1004, builder.build());

关键点

媒体通知必须配合前台Service使用,否则App被杀死后通知也会消失。Android 13开始,媒体通知有专门的「媒体播放」通道,优先级更高,用户无法关闭。

嗯,这里要注意一点:setShowActionsInCompactView()这个方法决定了在通知栏只显示一行时,哪些按钮可见。我一般把播放/暂停放在索引0,这样用户一眼就能看到核心控制。

其实媒体通知还有一个隐藏技巧:如果你用MediaSession,系统会自动帮你同步锁屏界面的控制。用户锁屏后也能切歌,体验非常棒。我在做音乐App时,就靠这个功能拿到了产品经理的表扬(笑)。

好了,通知交互这块就讲到这里。Action按钮让通知可操作,RemoteInput让通知可输入,进度条让通知可感知,MediaStyle让通知可控制——这四个组合起来,基本覆盖了90%的交互场景。实际项目中,你可能会把它们混着用,比如「下载进度条+取消按钮」,或者「消息通知+回复Action」。灵活组合才是王道。


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