点击操作后如何取消通知

179

从API level 16(Jelly Bean)开始,可以使用以下方式向通知添加操作:

builder.addAction(iconId, title, intent);

但是,当我为通知添加一个操作,并按下该操作时,通知不会被解除。 当单击通知本身时,它可以被关闭。

notification.flags = Notification.FLAG_AUTO_CANCEL;
或者
builder.setAutoCancel(true);

但显然,这与通知相关的操作无关。

有什么提示吗?或者这还没有成为API的一部分吗?我没有找到任何信息。

10个回答

185

当你在通知管理器上调用notify时,你为它提供了一个id - 这是你可以后续使用的唯一标识符(这是来自于通知管理器的):

notify(int id, Notification notification)

要取消,您需要调用:

cancel(int id)
与相同的id。 所以,基本上,您需要跟踪该id或可能将id放入添加到PendingIntent内部的Intent Bundle中?

33
谢谢,这解决了我的问题。不过,我还是觉得有点过于复杂了。与其只提供一个API来在动作被按下时自动关闭通知,你必须使用意图和通知ID传递来实现相同的功能。 - endowzoner
3
如果你觉得这很复杂,那就别去看如何更新通知(不要丢失那个 ID),或者检查通知是否显示(API 不会跟踪它,需要自己检查)。 - Travis
3
@Daksh:基本上,当你的操作被按下时,你需要将通知标签和ID添加到你的意图中。有了这些额外的信息,你可以在启动活动中检查它是否是通过通知操作启动的。 - endowzoner
6
onCreate()中的代码示例: Bundle extras = getIntent().getExtras(); if (extras != null) { String tag = extras.getString(NotificationReceiver.INTENT_EXTRA_NOTIFICATION_TAG); int id = extras.getInt(NotificationReceiver.INTENT_EXTRA_NOTIFICATION_ID); if (NotificationReceiver.NOTIFICATION_ID == id && NotificationReceiver.NOTIFICATION_TAG.equals(tag)) { // 通过通知操作启动了Activity,取消该通知 NotificationManager manager = (NotificationManager) getSystemService(Service.NOTIFICATION_SERVICE); manager.cancel(tag, id); } } - endowzoner
4
新的API中,你可以使用notify(String tag, int id, Notification notification)函数来通知用户,并相应地使用cancel(String tag, int id)函数来取消通知。请注意,翻译时需要保证内容不失原意,同时使其更加易懂,不得添加解释或其他无关内容。 - Malachiasz
显示剩余4条评论

71

使用Lollipop的头部显示通知时发现了这个问题。请参考设计指南。下面是实现此功能的完整代码(尽可能完整)。

直到现在,“关闭”按钮并不是很重要,但现在它更加显眼了。

heads up notification

构建通知

int notificationId = new Random().nextInt(); // just use a counter in some util class...
PendingIntent dismissIntent = NotificationActivity.getDismissIntent(notificationId, context);

NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setPriority(NotificationCompat.PRIORITY_MAX) //HIGH, MAX, FULL_SCREEN and setDefaults(Notification.DEFAULT_ALL) will make it a Heads Up Display Style
        .setDefaults(Notification.DEFAULT_ALL) // also requires VIBRATE permission
        .setSmallIcon(R.drawable.ic_action_refresh) // Required!
        .setContentTitle("Message from test")
        .setContentText("message")
        .setAutoCancel(true)
        .addAction(R.drawable.ic_action_cancel, "Dismiss", dismissIntent)
        .addAction(R.drawable.ic_action_boom, "Action!", someOtherPendingIntent);

// Gets an instance of the NotificationManager service
NotificationManager notifyMgr = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

// Builds the notification and issues it.
notifyMgr.notify(notificationId, builder.build());

通知活动(NotificationActivity)

public class NotificationActivity extends Activity {

    public static final String NOTIFICATION_ID = "NOTIFICATION_ID";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        manager.cancel(getIntent().getIntExtra(NOTIFICATION_ID, -1));
        finish(); // since finish() is called in onCreate(), onDestroy() will be called immediately
    }

    public static PendingIntent getDismissIntent(int notificationId, Context context) {
        Intent intent = new Intent(context, NotificationActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        intent.putExtra(NOTIFICATION_ID, notificationId);
        PendingIntent dismissIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
        return dismissIntent;
    }

}

AndroidManifest.xml(用于防止SystemUI聚焦到后台堆栈所需的属性)

<activity
    android:name=".NotificationActivity"
    android:taskAffinity=""
    android:excludeFromRecents="true">
</activity>

我有一个应用程序发来的多个通知,这些通知被设置为持续通知。当特定通知上执行操作时,我想清除该通知。 - Prasad
3
在这里使用BroadcastReceiver来取消通知是否更有效?这是一个很好的例子,展示了如何实现,但还可以进一步简化:https://dev59.com/82Ij5IYBdhLWcg3w8ZQF#19745745 - alice.harrison
1
解决方案有效,但是extras集合与意图一起转发。它们没有转发到onCreate方法。一种方法是使用静态变量。有人知道为什么意图extras没有被转发吗? - Baschi
为什么只有将getDismissIntent放置在NotificationActivity中才能正常工作?如果PendingIntent创建代码放置在通知构建器类中,dismiss Intent就无法正常工作。我刚刚花了2个小时解决这个问题,但我无法弄清楚为什么必须在Activity中创建Pending Intent。有人能解释一下为什么会这样吗? - Ray Li
getDismissIntent()是一个静态的“辅助”函数,用于构建正确的Intent以与NotificationActivity通信。因此,这些通常与Activity捆绑在一起。但只要您小心地设置NOTIFICATION_ID和上下文,我认为这个静态函数也可以放置在通知构建器类中。 - Mike

20

我发现在扩展通知中使用操作按钮时,您需要编写额外的代码,并受到更多限制。

当用户点击操作按钮时,您必须手动取消通知。通知仅自动取消默认操作。

此外,如果您从按钮启动广播接收器,则通知抽屉不会关闭。

最终,我创建了一个新的NotificationActivity来解决这些问题。这个没有任何UI的中间活动取消通知,然后启动我真正想从通知中启动的活动。

我在相关帖子“单击Android通知操作不会关闭通知抽屉”中发布了示例代码。


2
很遗憾,他们仍然没有将这个功能集成到API中...像这样做相当不专业。但仍然是唯一的方法,特别是如果您无法控制目标意图,比如查看URL。 - Bogdan Zurac
谢谢你提供的信息,你是正确的。不过我会使用IntentService而不是中间活动。 - Tim

10

你是对的!虽然cancel()函数有两个实现方式,一个带有TAG,一个没有。但我们必须提供一个TAG。这里是来自文档的cancel函数:public void cancel(@Nullable String tag, int id)。最后在Android Q上进行了检查。 - sud007

10
在你的意图触发后,你需要运行以下代码以移除通知。
NotificationManagerCompat.from(this).cancel(null, notificationId);

注意:notificationId 是传递给运行通知的相同 ID。

9

在我看来,使用BroadcastReceiver是取消通知的更清晰的方式:

在 AndroidManifest.xml 中:

<receiver 
    android:name=.NotificationCancelReceiver" >
    <intent-filter android:priority="999" >
         <action android:name="com.example.cancel" />
    </intent-filter>
</receiver>

在Java中的文件操作:

Intent cancel = new Intent("com.example.cancel");
PendingIntent cancelP = PendingIntent.getBroadcast(context, 0, cancel, PendingIntent.FLAG_CANCEL_CURRENT);

NotificationCompat.Action actions[] = new NotificationCompat.Action[1];

取消通知接收器

public class NotificationCancelReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        //Cancel your ongoing Notification
    };
}

1
这就是我所做的,但是你如何从onReceive方法中获取通知ID(在此示例中为0)?它不在Intent中,因为它没有被添加到其中。我尝试将其作为额外添加,但似乎真正的通知ID并不是我在创建活动中添加的那个额外的ID... :-/ - Marco Zanetti
我非常喜欢使用广播服务而不是活动的这种方法,我认为它更加轻量级。 - Christophe Moine
但是我必须使用<intent.setAction(Integer.toString(notificationId));>,以便能够解除显示的任何通知。 - Christophe Moine
1
@MarcoZanetti 你需要生成一个通知 ID ,并将其传递给待定意图和发送通知时的 notify 方法。如果这样做,当用户单击取消操作时,它将调用广播接收器,然后您可以从额外的部分获取通知 ID 。 - Ray Hunter
@ChristopheMoine 你可以通过 intent.putExtra() 设置 id,然后在 BroadcastReceiver 中获取它。 - Vadim Kotov
这很糟糕 - 这意味着当用户想要执行某些操作时,我的应用程序需要捕获通知中的每个动作,例如[分享]。 - Someone Somewhere

7

你总是可以从被操作触发的内容中(例如,在与你提供给 addAction()PendingIntent 相关联的活动的 onCreate() 中),使用 cancel() 方法取消 Notification


2
但是我如何在被调用的活动中访问通知? - endowzoner
@FleshWound:cancel()方法需要传入你调用notify()时所使用的通知ID,而不是通知对象本身。 - CommonsWare
如果设置了setGroup并且存在组摘要通知,则@CommonsWare cancel(id)将停止工作。在这种情况下,由于某种原因,取消操作不起作用。没有组摘要时,取消操作可以正常工作。 - Kushan
如果我的待定意图是ACTION_VIEW,类型为image/jpeg(与另一个应用程序共享图像),那么取消操作应该如何触发?在我看来,Android 应该自动取消,我对 Android 为什么不处理它感到困惑! - Someone Somewhere
@SomeoneSomewhere:“那么取消应该如何触发?”--它不能。虽然没有阻止您在与“PendingIntent”相关的“Notification”中指向第三方应用程序,但这不是设计工作方式的真正方式,因此您将遇到此类问题。“在我看来,Android应该自动取消”--我可以看到在操作上提供一个标志,但它不应该是一种全时性的东西。如果是这样,跳过音乐播放器通知中的曲目将关闭通知。 - CommonsWare
我最终实现的是:通知操作触发IntentService,该IntentService取消通知并执行以下操作之一:1)创建新意图并触发意图选择器;2)在应用程序中执行特定操作。 - Someone Somewhere

6
只需加入这行代码:
 builder.setAutoCancel(true);

完整的代码如下:

NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
    builder.setSmallIcon(android.R.drawable.ic_dialog_alert);
    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.google.co.in/"));
    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
    builder.setContentIntent(pendingIntent);
    builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.misti_ic));
    builder.setContentTitle("Notifications Title");
    builder.setContentText("Your notification content here.");
    builder.setSubText("Tap to view the website.");
    Toast.makeText(getApplicationContext(), "The notification has been created!!", Toast.LENGTH_LONG).show();

    NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    builder.setAutoCancel(true);
    // Will display the notification in the notification bar
    notificationManager.notify(1, builder.build());

1
当针对Android 9进行目标设置时,AutoCancel似乎没有任何效果(在针对Android 8.1进行目标设置时工作正常)。 - Alix
这里的区别在于行动的不同。 - Someone Somewhere
已在API 24、27、30、31上进行了测试,通知确实会在单击后关闭。 - DIRTY DAVE
奇怪的是,在我工作的某些项目中,该操作在代码的某个地方确实可以取消通知,而在另一个地方则无法取消。 - android developer

2

总的来说:

NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);

Intent intent = new Intent(context, MyNotificationReceiver.class);
intent.putExtra("Notification_ID", 2022);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
            context,
            0,
            intent,
            ...);

Notification notification = new NotificationCompat.Builder(...)
...    
.addAction(0, "Button", pendingIntent)
.build();

notificationManager.notify(2022, notification);

对于关闭通知,您有两个选项:

方法 1:(在MyNotificationReceiver中)

NotificationManager manager = (NotificationManager)
            context.getSystemService(NOTIFICATION_SERVICE);
    manager.cancel(intent.getIntExtra("Notification_ID", -1));

方法2: (在MyNotificationReceiver中)

NotificationManagerCompat manager = NotificationManagerCompat.from(context);
manager.cancel(intent.getIntExtra("Notification_ID", -1));

最后,在manifest文件中:

<receiver android:name=".MyNotificationReceiver" />

-3

builder.setAutoCancel(true);

已在Android 9上进行了测试。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接