Android Notification(通知)(含兼容问题)

随着Google对Notification的不断升级,所以必须考虑适配问题了。

在Android4.1之前(不包括Android4.1)

图片.png

在高SDK版本中, setLatestEventInfo已被弃用,并且现在九成九Android用户的系统都在4.4以上了,所以这种情况就不需要考虑了。

后来在Android4.1以上(包括4.1)谷歌推出了Notification.Builder(建造者模式)方式创建通知,但这种方式不支持4.1之前的版本

Notification.Builder builder = new Notification.Builder(MainActivity.this);

Google后来推出了NotificationCompat.Builder方式,为各种配置做兼容性处理。

所以Notification.Builder已经被NotificationCompat.Builder替代。

NotificationCompat.Builder builder = new NotificationCompat.Builder(MainActivity.this);

NotificationCompat.Builder的出现是为了

(1)setSmallIcon设置小图标

setSmallIcon必须设置,否则通知不会显示,所以最简单的通知如下

    NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
    NotificationCompat.Builder builder = new NotificationCompat.Builder(MainActivity.this);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {//Android4.1以上
        builder.setSmallIcon(R.mipmap.shennvguo1);
    }

它只设置了小图,图片效果如下

图片.png
图片.png

那么我们给他添加标题和内容

(2)setContentTitle和setContentText
        builder.setSmallIcon(R.mipmap.shennvguo1)
                .setContentTitle("我是通知的标题")//设置通知标题
                .setContentText("我是一个通知");//设置通知内容

效果如下

图片.png
(3)setContentIntent

当我们尝试点击这个通知的时候发现点击是没有效果的,要想点击的时候让通知消失那么还需要改进下代码

    //准备intent
    Intent clickIntent = new Intent(this, DemoActivity.class);
    PendingIntent clickPI = PendingIntent.getActivity(this, 1, clickIntent, PendingIntent.FLAG_CANCEL_CURRENT);

        builder.setSmallIcon(android.R.drawable.stat_sys_download_done)
                .setContentTitle("我是通知的标题")//设置通知标题
                .setContentText("我是一个通知")//设置通知内容
                .setContentIntent(clickPI);// 设置pendingIntent,点击通知时就会用到

setContentIntent(clickPI)就是让通知的点击效果生效,代码的意思是,点击通知时,跳转到另一个Activity。

效果如下:

34.gif
(4)setAutoCancel

点击通知之后发现通知没有自动消失,这不是我们想要的效果,我们需要的效果是,当点击通知时让通知自动消失

        builder.setSmallIcon(android.R.drawable.stat_sys_download_done)
                .setContentTitle("我是通知的标题")//设置通知标题
                .setContentText("我是一个通知")//设置通知内容
                .setContentIntent(clickPI)// 设置pendingIntent,点击通知时就会用到
                .setAutoCancel(true);//设为true,点击通知栏移除通知

setAutoCancel(true)可以使点击通知时自动消失。

需要重点注意的是,setAutoCancel需要和setContentIntent一起使用,否则无效。

(5)setDeleteIntent

当我们左滑或者右滑通知时,发现通知会消失,也就是说,如果手机没有经过特殊定制处理的话,一般有两种消失的行为,第一种是点击消失,第二种是滑动消失,有时候,如果有需要可以通过广播监听这两种事件:

    NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);

    //准备intent
    Intent clickIntent = new Intent(this, NotificationBroadcastReceiver.class);
    clickIntent.setAction("com.xxx.xxx.click");
    // 构建 PendingIntent
    PendingIntent clickPI = PendingIntent.getBroadcast(this, 1, clickIntent, PendingIntent.FLAG_CANCEL_CURRENT);

    //准备intent
    Intent cacelIntent = new Intent(this, NotificationBroadcastReceiver.class);
    cacelIntent.setAction("com.xxx.xxx.cancel");
    // 构建 PendingIntent
    PendingIntent cacelPI = PendingIntent.getBroadcast(this, 2, cacelIntent, PendingIntent.FLAG_CANCEL_CURRENT );

        builder.setSmallIcon(android.R.drawable.stat_sys_download_done)
                .setContentTitle("我是通知的标题")//设置通知标题
                .setContentText("我是一个通知")//设置通知内容
                .setContentIntent(clickPI)// 设置pendingIntent,点击通知时就会用到
                .setAutoCancel(true)//设为true,点击通知栏移除通知
                .setDeleteIntent(cacelPI);//设置pendingIntent,左滑右滑通知时就会用到

setDeleteIntent就是设置滑动消失行为的Intent。

public class NotificationBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if (action.equals("com.xxx.xxx.click")) {
            //处理点击事件
            System.out.println("click");
        }

        if (action.equals("com.xxx.xxx.cancel")) {
            //处理滑动清除和点击删除事件
            System.out.println("cancel");
        }
    }
}

静态注册

    <receiver android:name=".notification.NotificationBroadcastReceiver">
        <intent-filter>
            <action android:name="com.xxx.xxx.click"/>
            <action android:name="com.xxx.xxx.cancel"/>
        </intent-filter>
    </receiver>

发送广播是PendingIntent处理的,当我们点击通知或者滑动通知,通知消失的时候PendingIntent就会发送广播。

(6)setLargeIcon

setLargeIcon可以设置大图标,如果没有设置大图标,大图标的位置的图片将会是小图标的图片。

小图标:该图需要做成透明背景图

图片.png

大图标:该图不需要做成透明背景图

图片.png

PS:个别定制机对通知做了很大的处理,有些厂商对setSmallIcon和setLargeIcon接口做了处理,不管我们代码中设置什么图片,他的通知图标在某定制手机上就是APP默认图标。如果大家遇到这个问题请不要纠结,直接找产商处理。

(7)setNumber

显示在右边的数字

图片.png
(8)setOngoing

设置是否是正在进行中的通知,默认是false

如果设置成true,左右滑动的时候就不会被删除了,如果想删除可以在之。

(9)setOnlyAlertOnce

设置是否只通知一次,这个效果主要体现在震动、提示音、悬挂通知上。
默认相同ID的Notification可以无限通知,如果有震动、闪灯、提示音和悬挂通知的话可能会不断的打扰用户。
setOnlyAlertOnce的默认值是false,如果设置成true,那么一旦状态栏有ID为n的通知,再次调用notificationManager.notify(n, notification)时,将不会有震动、闪灯、提示音以及悬挂通知的提醒。

悬挂通知主要体现在5.0以上的手机上,稍后会讲解。

(10)setProgress

为通知设置设置一个进度条

setProgress(int max, int progress, boolean indeterminate)

max:最大值
progress:当前进度
indeterminate:进度是否确定

当进度确定的情况下

setProgress(100, 20, false);
图片.png

当进度不确定的情况下

setProgress(100, 20, true);
35.gif
(11)setStyle

为通知设置样式

NotificationCompat的样式主要有以下几点

图片.png
  • BigPictureStyle

          NotificationCompat.BigPictureStyle bigPictureStyle = new NotificationCompat.BigPictureStyle();
          bigPictureStyle.setBigContentTitle("一二三四五,上山打老虎!");
          bigPictureStyle.setSummaryText("我就是一个标题!");
          bigPictureStyle.bigPicture(BitmapFactory.decodeResource(getResources(), R.drawable.che));
          //bigPictureStyle.bigLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.che));
    


setStyle(bigPictureStyle);
图片.png
  • BigTextStyle

          String title = "我是通知的标题";
          String conttext = "我是一个通知";
          NotificationCompat.BigTextStyle bigTextStyle = new NotificationCompat.BigTextStyle();
          bigTextStyle.setBigContentTitle(title);
          bigTextStyle.setSummaryText(conttext);
          bigTextStyle.bigText("一二三四五,上山打老虎!");
    


setStyle(bigTextStyle);
图片.png
  • DecoratedCustomViewStyle

功能是自定义通知布局

        RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.activity_main);


                .setStyle(new NotificationCompat.DecoratedCustomViewStyle())
                .setCustomContentView(remoteViews);//设置悬挂通知和一般通知的布局

setCustomContentView可以用setCustomBigContentView和setCustomHeadsUpContentView替代

                .setStyle(new NotificationCompat.DecoratedCustomViewStyle())
                .setCustomBigContentView(remoteViews)//设置通知的布局
                .setCustomHeadsUpContentView(remoteViews);//设置悬挂通知的布局

效果如下:

通知布局

图片.png

悬挂通知布局(Android5.0以上显示)

图片.png
注意:
  1. 如果使用Notification.Builder
    setCustomBigContentView、setCustomHeadsUpContentView和setCustomContentView只能在Android7.0以后可以生效,7.0之前无效。
  2. 如果使用NotificationCompat.Builder
    setCustomBigContentView、setCustomHeadsUpContentView和setCustomContentView在任何版本都有效
  • InboxStyle

添加行

        NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
        inboxStyle.addLine("第一行");
        inboxStyle.addLine("第二行");
        inboxStyle.addLine("第三行");
        inboxStyle.addLine("第四行");
        inboxStyle.addLine("第五行");
        inboxStyle.addLine("第六行");
        inboxStyle.addLine("第七行");
        inboxStyle.addLine("第八行");
        inboxStyle.addLine("第九行");
        inboxStyle.setBigContentTitle(title);
        inboxStyle.setSummaryText(conttext);


setStyle(inboxStyle);
图片.png
  • MessagingStyle

我们看一下代码写法

图片.png

在现在高版本的SDK中,MessagingStyle(@NonNull CharSequence userDisplayName)已经过时了,查看其构造方法

    /** @deprecated */
    @Deprecated
    public MessagingStyle(@NonNull CharSequence userDisplayName) {
        this.mUser = (new android.support.v4.app.Person.Builder()).setName(userDisplayName).build();
    }

    public MessagingStyle(@NonNull Person user) {
        if (TextUtils.isEmpty(user.getName())) {
            throw new IllegalArgumentException("User's name must not be empty.");
        } else {
            this.mUser = user;
        }
    }

我们发现剩下没过时的方法已经没有什么研究价值了。

图片.png
(12)setDefaults

向通知添加声音、闪灯和震动效果,最简单、最一致的方式是使用当前的用户默认设置,使用defaults属性,可以组合Notification.DEFAULT_ALL、Notification.DEFAULT_SOUND添加声音。

setDefaults(Notification.DEFAULT_ALL);

其中参数属性分别为:

Notification.DEFAULT_VISIBLE //添加默认震动提醒 需要VIBRATE permission
Notification.DEFAULT_SOUND //添加默认声音提醒
Notification.DEFAULT_LIGHTS //添加默认三色灯提醒
Notification.DEFAULT_ALL //添加默认以上三种全部提醒
(13)setVibrate

设置使用震动模式

setVibrate(new long[] {3000,1000,500,700,500,300})//延迟3秒,然后震动1000ms,再延迟500ms,接着震动700ms,最后再延迟500ms,接着震动300ms。

如果setDefaults的属性设置的是Notification.DEFAULT_ALL或者Notification.DEFAULT_VISIBLE,setVibrate将无效,因为Notification.DEFAULT_ALL或者Notification.DEFAULT_VISIBLE会替代setVibrate。

如果想自定义震动模式,有想有闪灯和提示音,那么可以这样做

    .setVibrate(new long[] {3000,1000,500,700,500,300})//延迟3秒,然后震动1000ms,再延迟500ms,接着震动700ms,最后再延迟500ms,接着震动300ms。
    .setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_LIGHTS);
(14)setSubText

在通知上新增一行文本,使原本两行文本变成三行文本

setSubText("我是一个SubText");
图片.png
(16)setTicker

设置通知在第一次到达时在状态栏中显示的文本

效果如下:

36.gif

需要注意的是:一些手机是显示不了的,查了些资料都说5.0以上的手机显示不了,但是我手上的测试机系统是5.0.2的,setTicker是可以生效的。

(17)setUsesChronometer

设置是否显示时间计时,电话通知就会使用到。

37.gif
(18)setWhen

通知产生的时间,会在通知栏信息里显示,一般是系统获取到的时间

setWhen(System.currentTimeMillis());

如果没有设置也没有影响,通知默认是当前时间戳。

当然,我们可以设置自己的时间,比如:

setWhen(System.currentTimeMillis() - 100);

这样通知的时间就会变化了,非当前时间。

主要需要注意的是:

setWhen只是为通知设置时间戳,和是否显示时间没有任何关系,设置是否显示时间的方法是setShowWhen。

(19)setShowWhen

设置是否显示当前时间

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {//Android4.2以上
            builder.setShowWhen(true);
        }

setShowWhen只有在Android4.2以上的手机上才能生效,另外当已经设置了setUsesChronometer(true),则当前时间就显示不了(除非定制手机处理),默认情况下时间计时和时间戳的显示是在通知的同一区域。

(20)setExtras

为通知设置数据,在Android4.4新增了设置数据的入口。

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {//Android4.4以上
            Bundle bundle = new Bundle();
            builder.setExtras(bundle);
        }
(21)setGroup

设置该通知组的密钥,即确认为哪一组。

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {//Android4.4W以上
            if(index>=0 && index<=10){
                builder.setGroup("notification_test");//捆绑通知
            }else if(index>10 && index<=15){
                builder.setGroup("notification_ceshi");//捆绑通知
            }
        }

这个主要体现在Android7.0以上的手机上的捆绑通知或者折叠通知。文章后面有讲解。

(22)setGroupSummary

设置是否为一组通知的第一个显示,默认是false。

setGroup和setGroupSummary结合可以实现捆绑通知或者折叠通知,代码如下:

            if(index == 1){
                builder.setGroupSummary(true);//设置是否为一组通知的第一个显示
            }
            builder.setGroup("notification_test");//捆绑通知

目前只有在7.0以上的手机上才能看出效果

图片.png

下面说明一下我针对setGroupSummary和setGroup的测试结果:

  • 如果setGroupSummary和setGroup都不设置,Android7.0以上的时候会自动分组


    图片.png

其中第一个通知也能看到。

  • 如果只设置setGroup,但是不设置setGroupSummary的情况。

              builder.setGroup("notification_test");//捆绑通知
    
图片.png
  • 如果每个通知都设置了setGroupSummary(true),这样每个通知都作为主通知了,毫无意义。

  • 如果指定第一条通知为主通知

              if(index == 1){
                  builder.setGroupSummary(true);//设置是否为一组通知的第一个显示
              }
              builder.setGroup("notification_test");//捆绑通知
    

那么,效果如下:

图片.png

我们看到第一条通知的文本已经看不到了,因为第一条通知已经作为主通知了。

(23)setLocalOnly

设置此通知是否仅与当前设备相关。如果设置为true,通知就不能桥接到其他设备上进行远程显示。

(24)setSortKey

设置针对一个包内的通知进行排序的键值,键值是一个字符串,通知会按照键值的顺序排列。

排序的效果看下图

1552975000127.gif
    NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
    NotificationCompat.Builder builder = new NotificationCompat.Builder(MainActivity.this);
    builder.setSmallIcon(R.mipmap.shennvguo1);
(25)setColor

设置通知栏颜色(Android5.0新增)

            builder.setColor(Color.RED);

目前测试大多测试机没有效果,我的Android8.0的Oppo手机的效果如下:

图片.png

图片的效果可以看出,应用名称以及进度条都变成红色了,目前因为定制机的影响,setColor作用不是很大了。建议还是不要设置颜色值为好。

之后,在Android8.0之后,新增了setColorized方法,官方的解释是:启用通知的背景颜色设置,目前不清楚它的作用。

注意:
  1. 如果使用Notification.Builder

setColor只有在Android5.0之后有效。

  1. 如果使用NotificationCompat.Builder

setColor在任何版本都有效,无需做兼容处理。

(26)setCategory

设置通知类别

通知类别有

public static final String CATEGORY_CALL = "call";
public static final String CATEGORY_MESSAGE = "msg";
public static final String CATEGORY_EMAIL = "email";
public static final String CATEGORY_EVENT = "event";
public static final String CATEGORY_PROMO = "promo";
public static final String CATEGORY_ALARM = "alarm";
public static final String CATEGORY_PROGRESS = "progress";
public static final String CATEGORY_SOCIAL = "social";
public static final String CATEGORY_ERROR = "err";
public static final String CATEGORY_TRANSPORT = "transport";
public static final String CATEGORY_SYSTEM = "sys";
public static final String CATEGORY_SERVICE = "service";
public static final String CATEGORY_REMINDER = "reminder";
public static final String CATEGORY_RECOMMENDATION = "recommendation";
public static final String CATEGORY_STATUS = "status";

代码如下:

            if(index % 2 == 0){
                builder.setCategory(NotificationCompat.CATEGORY_CALL);
            }else if(index % 2 == 1){
                builder.setCategory(NotificationCompat.CATEGORY_EMAIL);
            }

效果如下:

图片.png

我们可以看下通知右边的数字,所有的奇数为一组,所有的偶数为一组。

目前测试,低于Android5.0的手机是无效的。

(27)setPublicVersion

设置安全锁屏下的通知

builder.setPublicVersion(notification);
图片.png

另外别忘了添加

builder.setVisibility(Notification.VISIBILITY_PUBLIC);

稍后会对setVisibility详细讲解。

有些手机又类似于这样的开关

图片.png

我们需要手动打开这个开关才能在锁屏状态下显示通知。

目前在在Android5.0以上的手机上测试通过,由于没有Android4.X的设备并且模拟器也不支持锁屏,所以就留个悬念吧。

(28)setVisibility(横幅)

设置通知的显示等级

builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)

在Android5.0之后新增了以下三种通知等级

VISIBILITY_PUBLIC 任何情况都会显示通知
VISIBILITY_PRIVATE 只有在没有锁屏时会显示通知 
VISIBILITY_SECRET 在安全锁和没有锁屏的情况下显示通知

以上已经演示了通知在锁屏状态下的显示,那么这里再演示以下悬挂通知

图片.png

悬挂通知,就是挂在屏幕顶端的通知。

这里需要说明一下,不是只要设置setVisibility(NotificationCompat.VISIBILITY_PUBLIC)就一定能显示悬挂通知,有些手机对通知做了很大的处理,就比如上面说到了,必须开启顶端通知的开关才能显示。

(29)setFullScreenIntent

响应紧急状态的全屏事件(例如来电事件),也就是说通知来的时候,跳过在通知区域点击通知这一步,直接执行fullScreenIntent代表的事件

setFullScreenIntent存在兼容问题。

(30)setActions

当Notification.Builder builder被NotificationCompat.Builder取代后,setActions被舍弃,NotificationCompat.Builder新增addAction方法。

addAction在文章后面讲到。

(31)setChronometerCountDown

当Notification.Builder builder被NotificationCompat.Builder取代后,setChronometerCountDown被舍弃。

(32)setChannelId

创建通知时指定channelID

Android8.0新增了通知渠道这个概念,如果没有设置,则通知无法在Android8.0的机器上显示。

代码如下:

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//Android 8.0以上
            String channelID = "1";
            String channelName = "通知测试";
            NotificationChannel channel = new NotificationChannel(channelID, channelName, NotificationManager.IMPORTANCE_HIGH);
            notificationManager.createNotificationChannel(channel);
            //创建通知时指定channelID
            builder.setChannelId(channelID);
        }

NotificationChannel是通知渠道的意思,channelID为通知渠道ID,channelName为通知渠道名称,NotificationManager.IMPORTANCE_HIGH为通知渠道的优先级。

通道相关的知识我们最后再讲。

(33)addAction(只支持Android7.0以上的手机)

添加内联回复的通知。

            //准备intent
            Intent replyPendingIntent = new Intent(this, NotificationBroadcastReceiver.class);
            replyPendingIntent.setAction("com.xxx.xxx.replypending");
            replyPendingIntent.putExtra("messageId", index);
            // 构建 PendingIntent
            PendingIntent replyPendingPI = PendingIntent.getBroadcast(this, 2, replyPendingIntent, PendingIntent.FLAG_UPDATE_CURRENT );

            builder.setRemoteInputHistory(new String[]{"这条通知可以点击下面按钮直接回复..."});
            //创建一个可添加到通知操作的 RemoteInput.Builder 实例。 该类的构造函数接受系统用作文本输入密钥的字符串。 之后,手持式设备应用使用该密钥检索输入的文本。
            RemoteInput remoteInput = new RemoteInput.Builder("key_text_reply")
                    .setLabel("回复")
                    .build();

            //使用 addRemoteInput() 向操作附加 RemoteInput 对象。
            NotificationCompat.Action action = new NotificationCompat.Action.Builder(R.mipmap.shennvguo2, "点击直接回复", replyPendingPI)
                    .addRemoteInput(remoteInput)
                    .build();

            //对通知应用操作。
            builder.addAction(action);

在内联输入框中输入想要回复的文字,点击发送之后会收到一个广播,广播的处理如下:

        //从内联回复检索用户输入

        int messageId = intent.getIntExtra("messageId", 0);

        Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
        if (remoteInput != null) {
            NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
            CharSequence message = remoteInput.getCharSequence("key_text_reply");
            NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "1");
            builder.setSmallIcon(R.mipmap.shennvguo1)
                    .setContentTitle("内联回复")//设置通知标题
                    .setContentText(message)//设置通知内容
                    .setAutoCancel(true)//设为true,点击通知栏移除通知
                    .setOngoing(false)//设置是否是正在进行中的通知,默认是false
                    .setOnlyAlertOnce(false);//设置是否只通知一次

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//Android 8.0以上
                String channelID = "1";
                String channelName = "通知测试";
                NotificationChannel channel = new NotificationChannel(channelID, channelName, NotificationManager.IMPORTANCE_DEFAULT );
                notificationManager.createNotificationChannel(channel);
                //创建通知时指定channelID
                builder.setChannelId(channelID);
            }
            Notification notification = builder.build();
            notificationManager.notify(messageId, notification);

回复之后会发送一个通知,通知ID和回复前一致。

38.gif
(34)setSettingsText

当Notification.Builder被NotificationCompat.Builder替代之后,setSettingsText已被舍弃。

(35)setBadgeIconType

设置角标Icon类型。

经过测试,这个方法是无效的,现在好多国内手机Launcher的角标和setBadgeIconType无关。

有关角标的适配方案文章后面会讲到

(36)setShortcutId

setShortcutId字面上也和Launcher的角标有关,但经过测试没有发现有什么用。

有关角标的适配方案文章后面会讲到

(37)setTimeoutAfter

设置超时时间,超时之后自动取消。

builder.setTimeoutAfter(5000);//设置超时时间,超时之后自动取消(Android8.0有效)

经过测试,setTimeoutAfter目前只有在8.0以上的手机上有效。

(38)setGroupAlertBehavior(8.0新增)

设置群组提醒的行为;

NotificationCompat.GROUP_ALERT_ALL:这意味着在有声音或振动的组中,所有通知都应该发出声音或振动,因此当该通知在组中时,不会将其静音
NotificationCompat.GROUP_ALERT_SUMMARY:即使将一个组中的摘要通知发布到具有声音和/或振动的通知通道中,也应使其静音(无声音或振动)
NotificationCompat.GROUP_ALERT_CHILDREN:一个组中的所有子通知都应该被静音(没有声音或振动),即使他们被发布到有声音和/或振动的通知频道。如果此通知是子组级,则使用此常量将此通知静音。这必须应用于所有要静音的子通知。

(39)setSound

设置铃声。

setSound(Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.publicnotify))

setDefaults(Notification.DEFAULT_SOUND) //获取默认铃声
setSound(Uri.parse("file:///sdcard/xx/xx.mp3")) //获取自定义铃声
setSound(Uri.withAppendedPath(Audio.Media.INTERNAL_CONTENT_URI, "5")) //获取Android多媒体库内的铃声
(40)setLights

设置呼吸灯。

setLights(Color.RED,2000,Color.BLUE)

参数依次是:灯光颜色, 亮持续时间,暗的时间,不是所有颜色都可以,这跟设备有关,有些手机还不带三色灯; 另外,还需要为Notification设置flags为Notification.FLAG_SHOW_LIGHTS才支持三色灯提醒!

Notification Channels

在Android8.0之后,引入了通知渠道。

简单的写法如下

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//Android 8.0以上
            String channelID = "1";
            String channelName = "我是通知渠道";
            NotificationChannel channel = new NotificationChannel(channelID, channelName, NotificationManager.IMPORTANCE_HIGH);
            //channel.setShowBadge(true);
            notificationManager.createNotificationChannel(channel);
            //创建通知时指定channelID
            builder.setChannelId(channelID);
            //builder.setTimeoutAfter(5000);//设置超时时间,超时之后自动取消(Android8.0有效)
        }

在Android8.0中,可以在通知中查看渠道信息。

图片.png
图片.png
(1)重要性
NotificationChannel channel = new NotificationChannel(channelID, channelName, NotificationManager.IMPORTANCE_MIN);

这里第三个参数传入的参数是重要性。

  • IMPORTANCE_NONE 关闭通知
  • IMPORTANCE_MIN 开启通知,不会弹出,但没有提示音,状态栏中无显示
  • IMPORTANCE_LOW 开启通知,不会弹出,不发出提示音,状态栏中显示
  • IMPORTANCE_DEFAULT 开启通知,不会弹出,发出提示音,状态栏中显示
  • IMPORTANCE_HIGH 开启通知,会弹出,发出提示音,状态栏中显示

在平时用的比较多了还是IMPORTANCE_HIGH,因为这个属性同样被定制机影响,这个只要了解就行,做系统级APP也许会深入的使用,普通的APP只要使用IMPORTANCE_HIGH就可以了。

(2)setShowBadge

显示通知角标。

channel.setShowBadge(true);//显示通知角标

oppo手机的角标分为两种:

  • 圆点角标(不显示数字)

当有新的通知时,显示圆点角标,但不显示数字

图片.png
  • 数字角标

当有新的通知时,显示数字角标,显示数字。

图片.png

需要注意的是:该方法受到定制手机的影响。

(2)canShowBadge

应该是检查设备是否支持显示角标

(3)setBypassDnd

设置可以绕过请勿打扰模式。

            channel.setBypassDnd(true);// 设置绕过请勿打扰模式

需要注意的是:该方法是无法生效的,只能被系统或者排序排序服务所更改。

(4)canBypassDnd

判断设备是否支持绕过免打扰。

(5)enableLights

设置通知出现时的闪灯(如果 android 设备支持的话)

            channel.enableLights(true);//设置通知出现时的闪灯(如果 android 设备支持的话)
(6)enableVibration

设置通知出现时的震动(如果 android 设备支持的话)

            channel.enableVibration(true);// 设置通知出现时的震动(如果 android 设备支持的话)
(7)setDescription

设置渠道的描述信息。

         channel.setDescription("AAAAAAAAAA");//设置渠道的描述信息
图片.png

一些定制手机将这个信息去除了。

(8)setImportance

设置重要性。(在前面说过了)

(9)setLightColor

设置呼吸灯的颜色。

需要设备的支持,有些手机呼吸灯只支持一种颜色。

(10)setName

设置渠道名称。

            channel.setName("wweqw");
图片.png
图片.png
(11)setSound

设置提示铃声。

channel.setSound(Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.publicnotify), new AudioAttributes.Builder().build());
(12)setVibrationPattern

设置震动模式。

channel.setVibrationPattern(new long[]{200, 200, 1000, 200, 1000, 200});
(13)setLockscreenVisibility

设置锁屏的情况下是否显示通知。

需要注意的是:该方法是无法生效的,只能被系统或者排序排序服务所更改。

app只要使用

        //锁屏时显示通知
        builder.setPublicVersion(notification);

即可。

数字角标

随着国内定制手机的大量出现,Launcher上的角标问题越来越棘手。

即使我们在代码中添加了setBadgeIconType、setShortcutId、channel.setShowBadge之类的配置也没有用。

在网上找了些框架,比如
https://github.com/leolin310148/ShortcutBadger,其实也有时问题的,并不能适配所有的机型,这也体现了国内设备的恶心之处。
大家先关注一下吧,目前好几款手机都测试OK的。

这里有一篇文章:

[贝聊科技]有关Android应用桌面角标(BadgeNumber)实现的探讨

这篇文章算是比较好的了,大家可以去看看,oppo手机的角标我试了,有用。

由于缺少其他机型的手机,所以就没有尝试了。

另外,有关角标,有一点最最重要,好多定制手机做了通知角标白名单,只有将你app的包名放入定制手机的 白名单里面才可以显示角标,“com.xunmeng.pinduoduo”这个包名已被大部分定制手机假如白名单,大家可以将包名改成这个试试。实在不行大家就在网上搜搜QQ和微信的包名是什么,然后将自己app的包名改掉就行了。

最后附上通知适配的通用写法:

int index = 0;

/**
 * 弹出通知提醒
 */
private void tapNotification(){
    index = index +1 ;
    NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);

    //准备intent
    Intent clickIntent = new Intent(this, NotificationBroadcastReceiver.class);
    clickIntent.setAction("com.xxx.xxx.click");
    // 构建 PendingIntent
    PendingIntent clickPI = PendingIntent.getBroadcast(this, 1, clickIntent, PendingIntent.FLAG_CANCEL_CURRENT);

    //准备intent
    Intent cacelIntent = new Intent(this, NotificationBroadcastReceiver.class);
    cacelIntent.setAction("com.xxx.xxx.cancel");
    // 构建 PendingIntent
    PendingIntent cacelPI = PendingIntent.getBroadcast(this, 2, cacelIntent, PendingIntent.FLAG_CANCEL_CURRENT );

    //准备intent
    Intent fullscreenIntent = new Intent(this, NotificationBroadcastReceiver.class);
    fullscreenIntent.setAction("com.xxx.xxx.fullscreen");
    // 构建 PendingIntent
    PendingIntent fullscreenPI = PendingIntent.getBroadcast(this, 2, fullscreenIntent, PendingIntent.FLAG_UPDATE_CURRENT );

    String channelID = "1";

    //Notification.Builder builder = new Notification.Builder(MainActivity.this);
    NotificationCompat.Builder builder = new NotificationCompat.Builder(MainActivity.this, channelID);

    String title = "标题"+index;
    String conttext = "我是一个通知"+index;
    //NotificationCompat.BigTextStyle bigTextStyle = new NotificationCompat.BigTextStyle();
    //bigTextStyle.setBigContentTitle(title);
    //bigTextStyle.setSummaryText(conttext);
    //bigTextStyle.bigText("一二三西思思");
    //builder.setStyle(bigTextStyle);

    //NotificationCompat.MessagingStyle messagingStyle = new NotificationCompat.MessagingStyle("UserName");
    //messagingStyle.addMessage("message",System.currentTimeMillis(),"JulyYu");
    //messagingStyle.setConversationTitle("Messgae Title");
    //builder.setStyle(messagingStyle);


    //NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
    //inboxStyle.setBigContentTitle(title);
    //inboxStyle.setSummaryText(conttext);
    //inboxStyle.addLine("A");
    //inboxStyle.addLine("B");


    RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.activity_main);
    builder.setSmallIcon(R.mipmap.shennvguo1)
            .setContentTitle(title)//设置通知标题
            .setContentText(conttext)//设置通知内容
            .setContentIntent(clickPI)// 设置pendingIntent,点击通知时就会用到
            .setAutoCancel(true)//设为true,点击通知栏移除通知
            .setDeleteIntent(cacelPI)//设置pendingIntent,左滑右滑通知时就会用到
            .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.shennvguo2))//设置大图标
            .setNumber(index)//显示在右边的数字
            .setOngoing(false)//设置是否是正在进行中的通知,默认是false
            .setOnlyAlertOnce(false)//设置是否只通知一次
            .setProgress(100, 20, false)
            //.setStyle(messagingStyle)
            .setSound(Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.publicnotify))
            .setVibrate(new long[] {3000,1000,500,700,500,300})//延迟3秒,然后震动1000ms,再延迟500ms,接着震动700ms,最后再延迟500ms,接着震动300ms。
            .setLights(Color.RED,2000,Color.BLUE)
            //.setDefaults(Notification.DEFAULT_LIGHTS)
            .setSubText("我是一个SubText")
            .setTicker("通知测试")//提示
            .setUsesChronometer(true)
            .setWhen(System.currentTimeMillis())
            .setLocalOnly(true)//设置此通知是否仅与当前设备相关。如果设置为true,通知就不能桥接到其他设备上进行远程显示。
            .setShowWhen(true);
    if(index % 5 == 0){
        builder.setSortKey("A");//设置针对一个包内的通知进行排序的键值
    }else if(index % 5 == 1){
        builder.setSortKey("B");//设置针对一个包内的通知进行排序的键值
    }else if(index % 5 == 2){
        builder.setSortKey("C");//设置针对一个包内的通知进行排序的键值
    }else if(index % 5 == 3){
        builder.setSortKey("D");//设置针对一个包内的通知进行排序的键值
    }else if(index % 5 == 4){
        builder.setSortKey("E");//设置针对一个包内的通知进行排序的键值
    }
    //响应紧急状态的全屏事件(例如来电事件),也就是说通知来的时候,跳过在通知区域点击通知这一步,直接执行fullScreenIntent代表的事件
    //builder.setFullScreenIntent(fullscreenPI, true);
    //Bundle bundle = new Bundle();
    //builder.setExtras(bundle);
    //                if(index % 2 == 0){
    //                    builder.setCategory(NotificationCompat.CATEGORY_CALL);
    //                }else if(index % 2 == 1){
    //                    builder.setCategory(NotificationCompat.CATEGORY_EMAIL);
    //                }

    builder.setVisibility(Notification.VISIBILITY_PUBLIC);//悬挂通知(横幅)

    builder.setCustomBigContentView(remoteViews)//设置通知的布局
            .setCustomHeadsUpContentView(remoteViews)//设置悬挂通知的布局
            .setCustomContentView(remoteViews);
    //builder.setChronometerCountDown()//已舍弃

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {//Android7.0以上

        //                if(index == 1){
        //                    builder.setGroupSummary(true);//设置是否为一组通知的第一个显示
        //                }
        //                builder.setGroup("notification_test");//捆绑通知


        //准备intent
        Intent replyPendingIntent = new Intent(this, NotificationBroadcastReceiver.class);
        replyPendingIntent.setAction("com.xxx.xxx.replypending");
        replyPendingIntent.putExtra("messageId", index);
        // 构建 PendingIntent
        PendingIntent replyPendingPI = PendingIntent.getBroadcast(this, 2, replyPendingIntent, PendingIntent.FLAG_UPDATE_CURRENT );

        builder.setRemoteInputHistory(new String[]{"这条通知可以点击下面按钮直接回复..."});
        //创建一个可添加到通知操作的 RemoteInput.Builder 实例。 该类的构造函数接受系统用作文本输入密钥的字符串。 之后,手持式设备应用使用该密钥检索输入的文本。
        RemoteInput remoteInput = new RemoteInput.Builder("key_text_reply")
                .setLabel("回复")
                .build();

        //使用 addRemoteInput() 向操作附加 RemoteInput 对象。
        NotificationCompat.Action action = new NotificationCompat.Action.Builder(R.mipmap.shennvguo2, "点击直接回复", replyPendingPI)
                .addRemoteInput(remoteInput)
                .build();

        //对通知应用操作。
        builder.addAction(action);

    }

    //builder.setBadgeIconType(NotificationCompat.BADGE_ICON_NONE);//设置角标类型(无效)
    //builder.setSettingsText();已舍弃
    //builder.setShortcutId("100");
    //builder.setColorized(true);//启用通知的背景颜色设置
    //builder.setColor(Color.RED);

    //builder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//Android 8.0以上
        String channelName = "我是通知渠道";
        NotificationChannel channel = new NotificationChannel(channelID, channelName, NotificationManager.IMPORTANCE_HIGH);
        //channel.setShowBadge(true);//显示通知角标
        //boolean aa = channel.canShowBadge();
        //channel.setBypassDnd(true);// 设置绕过请勿打扰模式
        //boolean ca = channel.canBypassDnd();
        channel.enableLights(true);//设置通知出现时的闪灯(如果 android 设备支持的话)
        channel.enableVibration(true);// 设置通知出现时的震动(如果 android 设备支持的话)
        channel.setDescription("AAAAAAAAAA");//设置渠道的描述信息
        //channel.setGroup("AAAA");
        channel.setImportance(NotificationManager.IMPORTANCE_HIGH);
        channel.setLightColor(Color.YELLOW);
        //channel.setLockscreenVisibility();
        channel.setName("wweqw");
        channel.setSound(Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.publicnotify), new AudioAttributes.Builder().build());
        channel.setVibrationPattern(new long[]{200, 200, 1000, 200, 1000, 200});
        notificationManager.createNotificationChannel(channel);
        //创建通知时指定channelID
        builder.setChannelId(channelID);
        //builder.setTimeoutAfter(5000);//设置超时时间,超时之后自动取消(Android8.0有效)
    }

    Notification notification = builder.build();
    //锁屏时显示通知
    builder.setPublicVersion(notification);
    notificationManager.notify(index, notification);
}
PS:

如果使用Notification.Builder,那么会遇到好多兼容性问题,以上代码的适配方案肯定行不通。
后来Google为了解决兼容性问题,将NotificationCompat.Builder替代Notification.Builder, 使用NotificationCompat.Builder不需要过多考虑适配问题,直接使用以上适配方案即可。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容