Android 中各种通知(Notification)的使用

通知通道(Channel)

从 Android 8.0 开始,显示通知必须先创建通道:

NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    NotificationChannel notificationChannel = NotificationFactory.createNotificationChannel();
    notificationManager.createNotificationChannel(notificationChannel);
}
@RequiresApi(api = Build.VERSION_CODES.O)
public static NotificationChannel createNotificationChannel() {
    String name = "通道名称";
    // 设置通道的重要性级别(Android 7.1 或更低需要使用 setPriority 方法)
    int importance = NotificationManager.IMPORTANCE_HIGH;
    return new NotificationChannel(CHANNEL_ID, name, importance);
}

在构建 Notification Builder 时也需要传入 Channel ID:

new NotificationCompat.Builder(context, CHANNEL_ID)

通知重要性级别

不同场景下的通知,其重要性也可能不同。下表中列出了不同的重要性级别,以及在不同系统版本上设置的方法:

级别 importance(Android 8.0 及以上) priority(Android 7.1 及以下)
紧急,发出声音并作为警告通知出现 IMPORTANCE_HIGH PRIORITY_HIGH 或者 PRIORITY_MAX
高,发出声音 IMPORTANCE_DEFAULT PRIORITY_DEFAULT
中,没有声音 IMPORTANCE_LOW PRIORITY_LOW
低,没有声音并且不会出现在状态栏中,并且通知会被折叠 IMPORTANCE_MIN PRIORITY_MIN

创建基本通知

基本通知包含小图标、标题和少量内容:

// 从 Android8.0 开始,必须设置 Channel ID
new NotificationCompat.Builder(context, CHANNEL_ID)
        .setSmallIcon(icon)
        .setContentTitle(title)
        .setContentText(content)
        // 设置优先级,优先级决定了 Android7.1 及更低版本通知的侵入程度
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .build();

大文本样式通知

new NotificationCompat.Builder(context, channelId)
        .setSmallIcon(icon)
        .setContentTitle(title)
        .setContentText(content)
        .setPriority(priority)
        .setStyle(new NotificationCompat.BigTextStyle())
        .build();

带图标样式通知

new NotificationCompat.Builder(context, channelId)
        .setSmallIcon(icon)
        .setContentTitle(title)
        .setContentText(content)
        .setPriority(priority)
        .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher))
        .build();

大图标样式通知

Bitmap iconBitmap = BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher);
Bitmap avatarBitmap = BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_avatar);
return new NotificationCompat.Builder(context, channelId)
        .setSmallIcon(icon)
        .setContentTitle(title)
        .setContentText(content)
        .setPriority(priority)
        .setLargeIcon(iconBitmap)
        .setStyle(new NotificationCompat.BigPictureStyle()
                .bigPicture(avatarBitmap))
        .build();

响应用户点击

可以通过设置 setContentIntent()方法来定义当用户点击通知后所执行操作:

Intent intent = new Intent(context, NotificationDetailActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
new NotificationCompat.Builder(context, channelId)
        .setSmallIcon(icon)
        .setContentTitle(title)
        .setContentText(content)
        .setPriority(priority)
        // 点击后自动移除通知
        .setAutoCancel(true)
        // 设置点击后响应的 PendingIntent
        .setContentIntent(pendingIntent)
        .build();

也可以通过 setDeleteIntent()方法设置当用户移除通知后所执行的操作。

另外还可以通过 setFullScreenIntent()方法设置「横幅通知」:

在这里插入图片描述

当通知具有较高的优先级并使用铃声或震动时,也可能会触发「横幅通知」。

该通知用于极高优先级的通知,会直接以横幅的形式显示给用户。如果是普通通知,不建议使用该方法,会打扰到用户。另外实际测试中,发现在某些厂商的定制 Rom,如华为 EMUI,需要在通知设置中打开横幅通知设置才行。另外如果应用定位于 AndroidQ 或以上,需要在 Manifest 中配置 Manifest.permission.USE_FULL_SCREEN_INTENT权限才能使用。

添加操作按钮

通知最多可添加三个按钮,用于快速响应用户的操作。这些按钮点击后的响应操作不应该与用户点击通知所响应的操作一样。

Intent intent = new Intent(context, NotificationDetailActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

Intent intentReceiver = new Intent(context, NotificationReceiver.class);
intentReceiver.setAction(NotificationReceiver.ACTION);
PendingIntent pendingIntentReceiver = PendingIntent.getBroadcast(context, 0, intentReceiver, 0);

new NotificationCompat.Builder(context, channelId)
        .setSmallIcon(icon)
        .setContentTitle(title)
        .setContentText(content)
        .setPriority(priority)
        .setContentIntent(pendingIntent)
        // 添加操作按钮(最多可添加三个)
        .addAction(android.R.drawable.sym_action_chat, "消息", pendingIntentReceiver)
        .build();

显示效果示例:
在这里插入图片描述

添加直接回复操作

从 Android7.0 (API 24)开始,用户可以直接在通知栏回复消息。这对于一些 IM 应用来说很有帮助:

// 创建广播,用于接收并处理用户回复的信息
Intent intent = new Intent(context, HandlerReplyMessageReceiver.class);
intent.setAction(HandlerReplyMessageReceiver.ACTION_HANDLER_REPLY_MESSAGE);

// 创建 PaddingIntent
int requestCode = 1; // 这里应该是当前会话 ID
PendingIntent pendingIntent = PendingIntent.getBroadcast(
        context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);

// 创建 RemoteInput
RemoteInput remoteInput = new RemoteInput.Builder(HandlerReplyMessageReceiver.KEY_REPLY_TEXT)
        .setLabel("回复")
        .build();

// 创建 action
NotificationCompat.Action action = new NotificationCompat.Action.Builder(android.R.drawable.sym_action_email, "回复", pendingIntent)
        .addRemoteInput(remoteInput)
        .build();

new NotificationCompat.Builder(context, channelId)
        .setSmallIcon(icon)
        .setContentTitle(title)
        .setContentText(content)
        .setPriority(priority)
        .addAction(action)
        .build();

在广播中接收用户消息并处理:

public class HandlerReplyMessageReceiver extends BroadcastReceiver {

    public static final String ACTION_HANDLER_REPLY_MESSAGE = "action_handler_reply_message";

    public static final String KEY_REPLY_TEXT = "key_reply_text";

    @Override
    public void onReceive(Context context, Intent intent) {
        if (!ACTION_HANDLER_REPLY_MESSAGE.equals(intent.getAction())) {
            return;
        }

        String replyMessage = getReplyMessage(intent);
        Log.d(MainActivity.TAG, "回复消息: " + replyMessage);
        Toast.makeText(context, "回复消息: " + replyMessage, Toast.LENGTH_SHORT).show();

        // 处理完用户回复的消息后,应更新通知,否则发送按钮会一直转圈
        MyNotificationManager.getInstance().cancel(7); // 这里的处理是直接不显示该条通知
    }

    // 获取用户在通知栏输入的回复消息
    private String getReplyMessage(Intent intent) {
        Bundle resultsFromIntent = RemoteInput.getResultsFromIntent(intent);
        if (resultsFromIntent != null) {
            return resultsFromIntent.getString(KEY_REPLY_TEXT);
        }
        return null;
    }

}

进度条通知

进度条通知可以分为两种,一种是知道确切进度的,一种是不确定进度的。

当知道确切的进度时:

int icon = android.R.drawable.stat_notify_sync;
String title = "有进度的进度条通知";
String content = "当知道确切进度时使用";

// 先创建基本的通知
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(icon)
        .setContentTitle(title)
        .setContentText(content)
        .setPriority(NotificationCompat.PRIORITY_LOW);

// 应该在非主线程中更新进度,为了演示方便,这里直接新起一个子线程
new Thread(() -> {
    int max = 100;
    int progress = 0;
    while (progress < max) {
        progress ++;
        builder.setProgress(max, progress, false);
        mNotificationManager.notify(8, builder.build());
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    // 执行完毕,删除进度条
    builder.setContentText("任务执行完毕");
    builder.setProgress(0, 0, false);
    mNotificationManager.notify(8, builder.build());
}).start();

不知道确定进度时:

int icon = android.R.drawable.stat_notify_sync;
String title = "不确定的进度条通知";
String content = "当不知道确切进度时使用";

// 先创建基本的通知
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
        .setSmallIcon(icon)
        .setContentTitle(title)
        .setContentText(content)
        .setPriority(NotificationCompat.PRIORITY_DEFAULT);

// 设置为不确定进度
builder.setProgress(0, 0, true);
mNotificationManager.notify(9, builder.build());

// 模拟一段时间后任务执行完毕
new Handler().postDelayed(() -> {
    // 删除进度条
    builder.setContentText("任务执行完毕");
    builder.setProgress(0, 0, false);
    mNotificationManager.notify(9, builder.build());
}, 5000);

设置分类

Android 为通知预置了一些分类。当你的通知设置了重要性较高的分类,那么即使用户设置了「勿扰模式」,用户也能收到通知,避免错过重要消息(实际测试中,没发现设置前后有啥区别,求解惑):

new NotificationCompat.Builder(context, channelId)
        .setSmallIcon(icon)
        .setContentTitle(title)
        .setContentText(content)
        .setPriority(priority)
        .setCategory(category)
        .build();

有以下分类:

  • CATEGORY_ALARM:闹钟或定时器。
  • CATEGORY_CALL:来电(语音或视频)或类似的同步通信请求。
  • CATEGORY_EMAIL:异步批量消息(电子邮件)。
  • CATEGORY_ERROR:后台操作或身份验证状态中的错误。
  • CATEGORY_EVENT:日历事件。
  • CATEGORY_MESSAGE:传入的直接消息(短信,即时消息等)。
  • CATEGORY_NAVIGATION:地图转弯导航。
  • CATEGORY_PROGRESS:长时间运行的后台操作的进度。
  • CATEGORY_PROMO:促销或广告。
  • CATEGORY_RECOMMENDATION:针对单个事物的特定,及时的建议。例如,新闻应用可能想要推荐它认为用户下次要阅读的新闻报道。
  • CATEGORY_REMINDER:用户预定提醒。
  • CATEGORY_SERVICE:运行后台服务的指示。
  • CATEGORY_SOCIAL:社交网络或共享更新。
  • CATEGORY_STATUS:有关设备或上下文状态的持续信息。
  • CATEGORY_SYSTEM:系统或设备状态更新。保留供系统使用。
  • CATEGORY_TRANSPORT:用于播放的媒体传输控制。

设置锁屏下通知可见性

通过 setVisibility()方法可以设置在锁屏下通知的可见性。有三种:

  • VISIBILITY_PUBLIC:显示完整的通知。
  • VISIBILITY_SECRET:在锁定屏幕上不显示通知的任何信息。
  • VISIBILITY_PRIVATE:只显示基本信息,例如通知的图标和内容标题,但隐藏通知内容。
new NotificationCompat.Builder(context, channelId)
        .setSmallIcon(icon)
        .setContentTitle(title)
        .setContentText(content)
        .setPriority(priority)
        .setVisibility(visibility)
        .build();

我在实际测试中,发现无论是使用原生系统(模拟器)还是第三方 ROM 系统。都没有任何效果。是我哪里姿势不对吗?

通知导航

当用户通过通知进入了应用内,此时用户点击返回键,根据场景的不同结果也会不同。可能是直接退出应用,也可能是回到应用主页。

针对这两种情况有不同的实现方式,直接退出应用:

Intent intent = new Intent(context, NotificationDetailActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

回到主页:

Intent intent = new Intent(context, NotificationDetailActivity.class);

TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(context);
// 添加到后退栈中
taskStackBuilder.addParentStack(NotificationDetailActivity.class);
// 添加到堆栈顶部
taskStackBuilder.addNextIntent(intent);

PendingIntent pendingIntent = taskStackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

new NotificationCompat.Builder(context, channelId)
        .setSmallIcon(icon)
        .setContentTitle(title)
        .setContentText(content)
        .setPriority(priority)
        .setStyle(new NotificationCompat.BigTextStyle())
        .setContentIntent(pendingIntent)
        .build();

manifest中需要配置 parentActivityName

<activity
        android:name=".activity.NotificationDetailActivity"
        android:parentActivityName=".activity.MainActivity"/>

实际测试中发现,回到首页该方法在个别手机上会无效。因此最好不要完全依赖该方法。

自定义通知

通知的内容高度,普通通知限制为 64dp,扩展通知限制为 256dp。

自定义通知内容布局

RemoteViews smallRemoteViews = new RemoteViews(context.getPackageName(), R.layout.notification_small);
smallRemoteViews.setTextViewText(R.id.notification_title, title);

Intent intent = new Intent(context, NotificationDetailActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
smallRemoteViews.setOnClickPendingIntent(R.id.notification_btn, pendingIntent);

new NotificationCompat.Builder(context, channelId)
        .setSmallIcon(icon)
        .setPriority(priority)
        .setStyle(new NotificationCompat.DecoratedCustomViewStyle())
        .setCustomContentView(smallRemoteViews)
        .build();

布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center">

    <TextView
            android:id="@+id/notification_title"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            tools:text="标题"
            style="@style/TextAppearance.Compat.Notification.Title"/>

    <Button
            android:id="@+id/notification_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button"/>

</LinearLayout>

注意:由于在不同的设备上,通知的背景都可能会不同。为了避免文字颜色与背景颜色相似导致看不清文字,最好如上面示例中一样,使用系统中的 style 样式。系统还有其他的 notification style,大家可以试一试。

运行效果:
在这里插入图片描述

完全自定义通知

RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.notification_big);
remoteViews.setTextViewText(R.id.notification, content);

new NotificationCompat.Builder(context, channelId)
        .setSmallIcon(icon)
        .setPriority(priority)
        .setCustomBigContentView(remoteViews)
        .build();
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_vertical"
        android:padding="16dp">

    <TextView
            android:id="@+id/notification"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            tools:text="内容"
            style="@style/TextAppearance.Compat.Notification.Info"/>

</LinearLayout>

在这里插入图片描述

通知 Badge

官方文档上说 8.0 开始,显示通知时可以同时显示 Badge。但我实际测试结果发现,只在原生系统上有效。国内厂商应该是都没有实现,比如在华为手机上就没有 Badge,想显示 Badge 只能用华为自己提供的 API 实现。所以说该功能比较鸡肋,暂时不谈,看以后都兼容了再说吧。

显示、更新通知

显示和更新通知都使用的是 notify()方法:

notify(int id, @NonNull Notification notification)

更新时只需要保证 id 相同即可。

通过 setOnlyAlertOnce()方法可以设置只在第一次时提醒,更新时不再发出声音和震动。

需要注意的是,不要太频繁的更新,比如说一秒内多次,系统可能会删除一些更新。

取消通知

应用主动取消通知有以下方法:

  • cancel():取消指定 ID 的通知。
  • cancelAll():取消当前应用所有显示的通知。
  • setTimeoutAfter():在指定的时间后取消通知。

注意事项

  • 从 Android 8.1 开始,如果在 1 秒内发送了多个通知,那么通知提醒音只会响一次。

参考文档

展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 黑客帝国 设计师: 上身试试
应支付0元
点击重新获取
扫码支付

支付成功即可阅读