Android通知栏-Notification(通知消息)

 

1.概述

    当应用程序在后台运行,希望向用户发出一些提示学习,就需要借助Notification(通知)来实现。在发出一条通知后,手机最上方的状态栏会显示一个通知的图标,下拉状态栏后就可以看到通知的详细内容。

1.1 视图分类

Notification有两种视觉风格,一种是标准视图(Normal View),另外一种是大视图(Big view)。标准视图在Android中各版本是通用的,但是对于大视图而言,仅支持Android4.1+的版本。 

1.2 标准视图

     从官方文档了解到,一个标准视图显示的大小保持在64dp高。如下图所示:

     其属性描述如下:

1代表: 通知标题 
2代表: 大图标 
3代表:  通知内容 
4代表: 通知消息 
5代表:小图标 
6代表: 通知时间,一般为系统时间,也可以使用setWhen()设置。

1.3 大视图

对于大视图(Big View)而言,它的细节区域只能显示256dp高度的内容,并且只对Android4.1+之后的设备才支持,它比标准视图不一样的地方,均需要使用setStyle()方法设定,它大致的效果如下:

Android为我们提供了三个实现类,用于显示不同的场景。分别是: 
Notification.BigPictureStyle, 在细节部分显示一个256dp高度的位图。 
Notification.BigTextStyle,在细节部分显示一个大的文本块。 
Notification.InboxStyle,在细节部分显示一段行文本

Notification.FLAG_SHOW_LIGHTS //三色灯提醒,在使用三色灯提醒时候必须加该标志符 
Notification.FLAG_ONGOING_EVENT //发起正在运行事件(活动中) 
Notification.FLAG_INSISTENT //让声音、振动无限循环,直到用户响应 (取消或者打开) 
Notification.FLAG_ONLY_ALERT_ONCE //发起Notification后,铃声和震动均只执行一次 
Notification.FLAG_AUTO_CANCEL //用户单击通知后自动消失 
Notification.FLAG_NO_CLEAR //只有全部清除时,Notification才会清除 ,不清楚该通知(QQ的通知无法清除,就是用的这个。还有百度通知栏里面的搜索框也是这个)。 
使用方法:在设置完属性后,设置 
Notification notification = builder.build(); 
notification.flags = Notification.FLAG_ONLY_ALERT_ONCE;
--------------------------------
原文:https://blog.csdn.net/dsc114/article/details/51721472 

2.基本用法

    通知可以在活动里面创建,也可以在广播接收器里面创建,也可以在服务里面创建。下面介绍创建通知的步骤:

2.1 创建一个NotificationManager

      创建一个NotificationManager来对通知进行管理。通过调用Context.getSystemService(String s)方法获取到NotificationManager实例对象,字符串s参数用于确定获取系统的哪个服务,这里传入Context.NOTIFICATION_SERVICE即可,如下所示:

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

     其中,Context.NOTIFICATION_SERVICE的值为:notification

public abstract class Context {
    ......
    public static final String NOTIFICATION_SERVICE = "notification";
}

 

2.2 使用Builder构造器来创建Notification对象

      在这里需要注意的是,为了解决API不稳定性问题和新老版本的兼容问题,使用support-v4提供的NotificationCompat类的Builder构造器来创建Notification对象,可以保证程序在所有的版本上都能正常工作。同时,Android8.0开始,废弃了Builder(@NonNull Context context)方法,改用public Builder(@NonNull Context context, @NonNull String channelId),如下所示:

//创建Notification,传入Context和channelId
Notification notification = new NotificationCompat.Builder(this, "chat")
                        .setAutoCancel(true)
                        .setContentTitle("收到聊天消息")
                        .setContentText("今天晚上吃什么")
                        .setWhen(System.currentTimeMillis())
                        .setSmallIcon(R.mipmap.ic_launcher)
                        .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
                        .setContentIntent(pendingIntent)
                        //在build()方法之前还可以添加其他方法
                        .build();

      Builder构造器其中一些方法说明如下:

setAutoCancel(boolean boolean) 设置点击通知后自动清除通知
setContent(RemoteView view) 设置自定义通知
setContentTitle(String string) 设置通知的标题内容
setContentText(String string) 设置通知的正文内容
setContentIntent(PendingIntent intent) 设置点击通知后的跳转意图
setWhen(long when) 设置通知被创建的时间
setSmallIcon(int icon)

设置通知的小图标

注意:只能使用纯alpha图层的图片进行设置,小图标会显示在系统状态栏上

setLargeIcon(Bitmap icon)

设置通知的大图标

下拉系统状态栏时就能看见

setPriority(int pri) 设置通知的重要程度
setStyle(Style style)

设置通知的样式

比如设置长文字、大图片等等

setVisibility(int defaults) 设置默认
setLight(int argb, int onMs, int offMs) 设置呼吸闪烁效果
setSound(Uri sound) 设置通知音效
setVibrate(long[] pattern)

设置震动效果,数组包含手机静止时长和震动时长

下标0代表手机静止时长

下标1代表手机整的时长

下标2代表手机静止时长

下标3,4,5.......以此类推

还需要在AndroidManifest.xml中声明权限:

<uses-permission android:name="android.permission.VIBRATE"/>

setColor(int argb) 设置通知栏颜色
setCategory(String category) 设置通知类别
setFullScreenIntent(PendingIntent intent, boolean b) 设置弹窗显示

    其中setPriority(int pri)方法参数一共有5个常量值可选,调用NotificationCompat的常量值,如下所示:

public class NotificationCompat {
    .......
    //默认的重要程度,和不设置效果是一样的
    public static final int PRIORITY_DEFAULT = 0;

    //最低的重要程度,系统可能只会在特定的场合显示这条通知
    public static final int PRIORITY_MIN = -2;
    
    //较低的重要程度,系统可能会将这类通知缩小,或改变其显示的顺序
    public static final int PRIORITY_LOW = -1;

    //较高的重要程度,系统可能会将这类通知放大,或改变其显示的顺序
    public static final int PRIORITY_HIGH = 1;

    //最高的重要程度,表示这类通知消息必须让用户看到,甚至做出响应
    public static final int PRIORITY_MAX = 2;


}

    注意:当设置最高重要程度后,其显示效果和QQ发送好友消息一样,如果正在其他APP内,消息会显示在屏幕上让用户看见

2.3 调用NotificationManager的notify(int id, Notification notification)让通知显示

        notify()方法接收两个参数,其中id表示每个通知所指定的id,要不一样。代码如下:

notificationManager.notify(1, notification);

        使用此方法前,需要将NotificationChannel(通知渠道创建出来)

2.4 让通知从状态栏消失方法

     有两种方法,一种是上面创建Notification时,添加setAutoCancel(true),另外一种就是通过动态代码方式,NotificationManager.cancel(int id),如下:

//传入对应通知的id
notificationManager.cancel(1);

 

3.NotificationChannel(通知渠道)

3.1 概述

       从Android 8.0系统开始,Google引入了通知渠道这个概念。

       什么是通知渠道呢?顾名思义,就是每条通知都要属于一个对应的渠道。每个App都可以自由地创建当前App拥有哪些通知渠道,但是这些通知渠道的控制权都是掌握在用户手上的。用户可以自由地选择这些通知渠道的重要程度,是否响铃、是否振动、或者是否要关闭这个渠道的通知

      即NotificationChannel 其实是把 Notification 分了个类别,设置不同优先级,开关之类的。如果你的 app 适配了的话,用户可以关掉不喜欢的通知,以提高用户体验。

       拥有了这些控制权之后,用户就再也不用害怕那些垃圾推送消息的打扰了,因为用户可以自主地选择自己关心哪些通知、不关心哪些通知。举个具体的例子,我希望可以即时收到支付宝的收款信息,因为我不想错过任何一笔收益,但是我又不想收到支付宝给我推荐的周围美食,因为我没钱只吃得起公司食堂。这种情况,支付宝就可以创建两种通知渠道,一个收支,一个推荐,而我作为用户对推荐类的通知不感兴趣,那么我就可以直接将推荐通知渠道关闭,这样既不影响我关心的通知,又不会让那些我不关心的通知来打扰我了。

      对于每个App来说,通知渠道的划分是非常需要仔细考究的,因为通知渠道一旦创建之后就不能再修改了,因此开发者需要仔细分析自己的App一共有哪些类型的通知,然后再去创建相应的通知渠道。

      这里我们来参考一下Twitter的通知渠道划分,如下所示:

     可以看到,Twitter就是根据自己的通知类型,对通知渠道进行了非常详细的划分,这样用户的自主选择性就比较高了,也就大大降低了用户不堪其垃圾通知的骚扰而将App卸载的概率。
--------------------- 
原文:https://blog.csdn.net/guolin_blog/article/details/79854070 

3.2 创建NotificationChannel

代码如下所示:

public class NotificationUtil {
    private static final int NOTIFICATION_MUSIC_ID = 10000;
    private static NotificationManager notificationManager;
    ......
    
    //初始化NotificationManager
    private static void initNotificationManager(Context context){
        if (notificationManager == null){
            notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);

        }
        //判断是否为8.0以上:Build.VERSION_CODES.O为26
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            //创建通知渠道ID
            String channelId = "musicNotification";
            //创建通知渠道名称
            String channelName = "音乐播放器通知栏";
            //创建通知渠道重要性
            int importance = NotificationManager.IMPORTANCE_DEFAULT;
            createNotificationChannel(context, channelId, channelName, importance);
        }
    }

    //创建通知渠道
    @TargetApi(Build.VERSION_CODES.O)
    private static void createNotificationChannel(Context context, String channelId, String channelName, int importance) {
        NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
        //channel有很多set方法
        ......
        //为NotificationManager设置通知渠道
        notificationManager.createNotificationChannel(channel);
    }


}

说明:这里传入的channelId要和创建的通知channelId一致,才能为指定通知建立通知渠道

1)NotificationChannel的方法列表

getId() 获取ChannelId
enableLights(boolean boolean) 是否开启指示灯(是否在桌面icon右上角展示小红点)
setLightColor() 设置指示灯颜色
enableVibration() 是否开启整的
setVibrationPattern()  设置震动频率
setImportance() 设置频道重要性
getImportance()  获取频道重要性
setSound()  设置声音
getSound()  获取声音
setGroup() 设置 ChannleGroup
getGroup()  得到 ChannleGroup
setBypassDnd()  设置绕过免打扰模式
canBypassDnd()  检测是否绕过免打扰模式
getName()  获取通知渠道名称
setLockScreenVisibility()  设置是否应在锁定屏幕上显示此频道的通知
getLockscreenVisibility()  检测是否应在锁定屏幕上显示此频道的通知
setShowBadge() 设置是否显示角标
canShowBadge()  检测是否显示角标

2)重要程度

     数值越高,提示权限就越高,最高的支持发出声音和悬浮通知,如下所示:

public class NotificationManager {
    ......
    public static final int IMPORTANCE_DEFAULT = 3;
    public static final int IMPORTANCE_HIGH = 4;
    public static final int IMPORTANCE_LOW = 2;
    public static final int IMPORTANCE_MAX = 5;
    public static final int IMPORTANCE_MIN = 1;
    public static final int IMPORTANCE_NONE = 0;
    public static final int IMPORTANCE_UNSPECIFIED = -1000;

}

3)发出通知

     调用NotificationManager的notify()方法即可

4)删除NotificationChann

     调用NotificationManager的deleteNotificationChannel(int chatChannelId)即可。

4.案例

1)xml布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">

    <Button
        android:id="@+id/chat"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="发送聊天通知"/>
</LinearLayout>

2)代码

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    private NotificationManager manager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            String channelId = "chat";
            String channelName = "聊天消息";
            int importance = NotificationManager.IMPORTANCE_HIGH;
            createNotificationChannel(channelId, channelName, importance);

            channelId = "subscribe";
            channelName = "订阅消息";
            importance = NotificationManager.IMPORTANCE_DEFAULT;
            createNotificationChannel(channelId, channelName, importance);
        }

        Button chat = findViewById(R.id.chat);
        chat.setOnClickListener(this);
        Button get = findViewById(R.id.get);
        get.setOnClickListener(this);
    }

    @TargetApi(Build.VERSION_CODES.O)
    private void createNotificationChannel(String channelId, String channelName, int importance) {
        NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
        NotificationManager notificationManager = (NotificationManager) getSystemService(
                NOTIFICATION_SERVICE);
        notificationManager.createNotificationChannel(channel);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.chat:    //聊天消息
                Notification notification = new NotificationCompat.Builder(this, "chat")
                        .setAutoCancel(true)
                        .setContentTitle("收到聊天消息")
                        .setContentText("今天晚上吃什么")
                        .setWhen(System.currentTimeMillis())
                        .setSmallIcon(R.mipmap.ic_launcher)
                        //设置红色
                        .setColor(Color.parseColor("#F00606"))
                        .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
                        .setContentIntent(pendingIntent)
                        .build();
                manager.notify(1, notification);
                break;
            case R.id.get:   //订阅消息
                Notification notificationGet = new NotificationCompat.Builder(this, "subscribe")
                        .setAutoCancel(true)
                        .setContentTitle("收到订阅消息")
                        .setContentText("新闻消息")
                        .setWhen(System.currentTimeMillis())
                        .setSmallIcon(R.mipmap.ic_launcher)
                        .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
                        .setContentIntent(pendingIntentGet)
                        .build();
                manager.notify(2, notificationGet);
                break;
        }
    }
}

3)效果

         

     注意这里出现了大图标和小图标,聊天消息之所以是红色,因为setColor为红色,详情见:

https://blog.csdn.net/guolin_blog/article/details/50945228

5.使用RemoteViews自定义Notification

        需要使用RemoteViews.RemoteViews描述了一个视图层次的结构,可以显示在另一个进程。

        RemoteViews提供了多个构造函数,一般使用RemoteViews(String packageName,int layoutId)。第一个参数为包的名称,第二个为layout资源的Id。当获取到RemoteViews对象之后,可以使用它的一系列setXxx()方法通过控件的Id设置控件的属性。最后使用NotificationCompat.Builder.setContent(RemoteViews)方法设置它到一个Notification中。 
 

6.PendingIntent

6.1 概述

         对于一个通知而言,它显示的消息是有限的,一般仅用于提示一些概要信息。但是一般简短的消息,并不能表达需要告诉用户的全部内容,所以需要绑定一个意图,当用户点击通知的时候,调用一个意图展示出一个Activity用来显示详细的内容。而Notification中,并不使用常规的Intent去传递一个意图,而是使用PendingIntent。 
        Intent和PendingIntent的区别:PendingIntent可以看做是对Intent的包装,通过名称可以看出PendingIntent用于处理即将发生的意图,而Intent用来用来处理马上发生的意图。而对于通知来说,它是一系统级的全局通知,并不确定这个意图被执行的时间。当在应用外部执行PendingIntent时,因为它保存了触发应用的Context,使得外部应用可以如在当前应用中一样,执行PendingIntent里的Intent,就算执行的时候响应通知的应用已经被销毁了,也可以通过存在PendingIntent里的Context照常执行它,并且还可以处理Intent说带来的额外信息。 

      因此可以将PendingIntent看做是延迟执行的Intent。

6.2 创建PendingIntent

     获取PendingInten实例可以根据需求从如下方法中获取:

PendingInteng.getBroadcast(contex, requestCode, intent, flags) 
PendingInteng.getService(contex, requestCode, intent, flags) 
PendingInteng.getActivity(contex, requestCode, intent, flags) 
PendingInteng.getActivities(contex, requestCode, intent, flags) 

其中flags属性参数用于确定PendingIntent的行为: 
FLAG_ONE_SHOT: 表示返回的PendingIntent仅能执行一次,执行完后自动消失 
FLAG_NO_CREATE: 表示如果描述的PendingIntent不存在,并不创建相应的PendingIntent,而是返回NULL 
FLAG_CANCEL_CURRENT: 表示相应的PendingIntent已经存在,则取消前者,然后创建新的PendingIntent 
FLAG_UPDATE_CURRENT: 表示更新的PendingIntent,如果构建的PendingIntent已经存在,则替换它,常用。

       获取到PendingIntent实例后,通过Builder构造器的setContentIntent(PendingIntent intent)方法,构建一个PendingIntent。
--------------------- 
原文:https://blog.csdn.net/dsc114/article/details/51721472 

6.3 使用案例

这里对目录4案例的代码进行改造,在代码中添加如下方法:

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    private NotificationManager manager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ......
    }

    @TargetApi(Build.VERSION_CODES.O)
    private void createNotificationChannel(String channelId, String channelName, int importance) {
       ......
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.chat:
                Intent intent = new Intent(this, NotificationActivity.class);
                PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);

                Notification notification = new NotificationCompat.Builder(this, "chat")
                        ......
                        .setContentIntent(pendingIntent)
                        .build();
                manager.notify(1, notification);
                break;

            case R.id.get:
                Intent intentGet = new Intent(this, NotificationActivity.class);
                PendingIntent pendingIntentGet = PendingIntent.getActivity(this, 0, intentGet, 0);

                Notification notificationGet = new NotificationCompat.Builder(this, "subscribe")
                        ......
                        .setContentIntent(pendingIntentGet)
                        .build();
                manager.notify(2, notificationGet);
                break;
        }
    }
}

       点击通知消息后,就可以跳转到NotificationActivity对应的界面了