美文网首页android UI自定义控件适配
Android Notification(通知)(含兼容问题)

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

作者: NoBugException | 来源:发表于2019-03-20 20:15 被阅读545次

    随着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不需要过多考虑适配问题,直接使用以上适配方案即可。

    相关文章

      网友评论

        本文标题:Android Notification(通知)(含兼容问题)

        本文链接:https://www.haomeiwen.com/subject/mfmtpqtx.html