对于通知栏的使用,Android各个版本其实都有比较大的调整。例如老版本的不兼容,大小图标问题以及自定义通知栏适配问题,这些都是比较头大的事,当然弄懂了就清楚了,本篇就处理下这些疑惑。

通知栏的使用

显示一个普通的通知栏

public static void showNotification(Context context) {    Notification notification = new NotificationCompat.Builder(context)            /**设置通知左边的大图标**/            .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher))            /**设置通知右边的小图标**/            .setSmallIcon(R.mipmap.ic_launcher)            /**通知首次出现在通知栏,带上升动画效果的**/            .setTicker("通知来了")            /**设置通知的标题**/            .setContentTitle("这是一个通知的标题")            /**设置通知的内容**/            .setContentText("这是一个通知的内容这是一个通知的内容")            /**通知产生的时间,会在通知信息里显示**/            .setWhen(System.currentTimeMillis())            /**设置该通知优先级**/            .setPriority(Notification.PRIORITY_DEFAULT)            /**设置这个标志当用户单击面板就可以让通知将自动取消**/            .setAutoCancel(true)            /**设置他为一个正在进行的通知。他们通常是用来表示一个后台任务,用户积极参与(如播放音乐)或以某种方式正在等待,因此占用设备(如一个文件下载,同步操作,主动网络连接)**/            .setOngoing(false)            /**向通知添加声音、闪灯和振动效果的最简单、最一致的方式是使用当前的用户默认设置,使用defaults属性,可以组合:**/            .setDefaults(Notification.DEFAULT_VIBRATE | Notification.DEFAULT_SOUND)            .setContentIntent(PendingIntent.getActivity(context, 1, new Intent(context, MainActivity.class), PendingIntent.FLAG_CANCEL_CURRENT))            .build();    NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);    /**发起通知**/    notificationManager.notify(0, notification);}

显示一个下载带进度条的通知

public static void showNotificationProgress(Context context) {    //进度条通知    final NotificationCompat.Builder builderProgress = new NotificationCompat.Builder(context);    builderProgress.setContentTitle("下载中");    builderProgress.setSmallIcon(R.mipmap.ic_launcher);    builderProgress.setTicker("进度条通知");    builderProgress.setProgress(100, 0, false);    final Notification notification = builderProgress.build();    final NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);    //发送一个通知    notificationManager.notify(2, notification);    /**创建一个计时器,模拟下载进度**/    Timer timer = new Timer();    timer.schedule(new TimerTask() {        int progress = 0;        @Override        public void run() {            Log.i("progress", progress + "");            while (progress <= 100) {                progress++;                try {                    Thread.sleep(100);                } catch (InterruptedException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }                //更新进度条                builderProgress.setProgress(100, progress, false);                //再次通知                notificationManager.notify(2, builderProgress.build());            }            //计时器退出            this.cancel();            //进度条退出            notificationManager.cancel(2);            return;//结束方法        }    }, 0);}

显示一个悬挂式的通知

悬挂式,部分系统厂商可能不支持。

public static void showFullScreen(Context context) {    NotificationCompat.Builder builder = new NotificationCompat.Builder(context);    Intent mIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://blog.csdn.net/linglongxin24"));    PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, mIntent, 0);    builder.setContentIntent(pendingIntent);    builder.setSmallIcon(R.mipmap.ic_launcher);    builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher));    builder.setAutoCancel(true);    builder.setContentTitle("悬挂式通知");    //设置点击跳转    Intent hangIntent = new Intent();    hangIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);    hangIntent.setClass(context, MainActivity.class);    //如果描述的PendingIntent已经存在,则在产生新的Intent之前会先取消掉当前的    PendingIntent hangPendingIntent = PendingIntent.getActivity(context, 0, hangIntent, PendingIntent.FLAG_CANCEL_CURRENT);    builder.setFullScreenIntent(hangPendingIntent, true);    NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);    notificationManager.notify(3, builder.build());}

显示一个折叠式的通知

public static void shwoNotify(Context context) {    //先设定RemoteViews    RemoteViews view_custom = new RemoteViews(context.getPackageName(), R.layout.view_custom);    //设置对应IMAGEVIEW的ID的资源图片    view_custom.setImageViewResource(R.id.custom_icon, R.mipmap.icon);    view_custom.setTextViewText(R.id.tv_custom_title, "今日头条");    view_custom.setTextColor(R.id.tv_custom_title, Color.BLACK);    view_custom.setTextViewText(R.id.tv_custom_content, "金州勇士官方宣布球队已经解雇了主帅马克-杰克逊,随后宣布了最后的结果。");    view_custom.setTextColor(R.id.tv_custom_content, Color.BLACK);    NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context);    mBuilder.setContent(view_custom)            .setContentIntent(PendingIntent.getActivity(context, 4, new Intent(context, MainActivity.class), PendingIntent.FLAG_CANCEL_CURRENT))            .setWhen(System.currentTimeMillis())// 通知产生的时间,会在通知信息里显示            .setTicker("有新资讯")            .setPriority(Notification.PRIORITY_HIGH)// 设置该通知优先级            .setOngoing(false)//不是正在进行的   true为正在进行  效果和.flag一样            .setSmallIcon(R.mipmap.icon);    Notification notify = mBuilder.build();    NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);    notificationManager.notify(4, notify);}

低版本不兼容处理

Android在appcompat-v7库中提供了一个NotificationCompat类来处理新老版本的兼容问题,我们在编写通知功能时都使用NotificationCompat这个类来实现,appcompat-v7库就会自动帮我们做好所有系统版本的兼容性处理了。一段基本的触发通知代码如下所示:

NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);NotificationCompat.Builder builder = new NotificationCompat.Builder(context);Notification notification = builder.setContentTitle("这是通知标题").setContentText("这是通知内容").setWhen(System.currentTimeMillis()).setSmallIcon(R.mipmap.ic_launcher).setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)).build();manager.notify(1, notification);

现在我们的app直接面对的设备一般都在android 5.0以上,所以也不需要做这种处理了。

大小图标问题

注意看一下我们给通知设置的图标,一个小图标、一个大图标,都是使用的R.mipmap.ic_launcher这张图,这在较低的编译版本上是没问题的,如果将targetSdkVersion指定成21或者更高的话,那么小图标则不可见(通知栏和大图的右下角有一个白白的圆),导致界面很不友好。

这到底是为什么呢?实际上,Android从5.0系统开始,对于通知栏图标的设计进行了修改。现在Google要求,所有应用程序的通知栏图标,应该只使用alpha图层来进行绘制,而不应该包括RGB图层(通俗点来讲,就是让我们的通知栏图标不要带颜色就可以了)。下边是支付宝和网易新闻的展示:


上图你会发现网易的图标更好看一些,因为系统给右下角的这个小圆圈默认是设置成灰色的,和我们的整体色调并不搭配,而网易则将这个小圆圈改成了红色,因此总体视觉效果更好。这种也很好处理,只需要在NotificationCompat.Builder中再多连缀一个setColor()方法就可以了:

Notification notification = builder    ......    .setColor(Color.parseColor("#EAA935"))    .build();

自定义通知栏

自定义通知需要定义一个layout文件,使用RemoteViews加载它并设置一些点击事件,再设置到builder,如下:

public void showNotification(){    NotificationCompat.Builder builder = new NotificationCompat.Builder(this);    builder.setSmallIcon(R.mipmap.small_launch_ic);        //自定义布局    RemoteViews rv = new RemoteViews(getPackageName(),R.layout.message);    rv.setTextViewText(R.id.tv,"有新通知了");    builder.setContent(rv);    //点击跳转    Intent intent = new Intent(this, MainAct.class);    PendingIntent pendingIntent = PendingIntent.getActivity(this, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);    remoteViews.setOnClickPendingIntent(R.id.root, pendingIntent);    Notification notification = builder.build();    NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);    notificationManager.notify(NOTIFICATION_ID,notification);}

认识RemoteViews

RemoteViews主要用在通知栏和桌面小部件上,简单来说RemoteViews是一个可以跨进程显示view的类,显示的view是从布局文件inflate出来,且该类提供了一些基本的方法来修改这个view的内容。

RemoteViews并不是一个view, 但可以表示一个layout的布局;又因为是继承parcelable,所以可以跨进程使用,但因为是跨进程,所以没办法像我们之前通过findviewById方法来访问布局里的每个view,所以RemoteViews提供了一些set方法来更新view 的显示,RemoteViews可以支持大部分系统控件,但是不支持自定义控件。

原理

自定义通知栏和桌面小部件,是由NotificationManager和AppWidgetmanager管理,而NotificationManager和AppWidgetManager是通过Binder分别和SystemServer进程中的NotificationManagerServer以及AppWidgetService进行通信,他们是运行在系统进程中,即SystemServer进程, 而我们是要在自身的应用进程中来更新远程系统进程的UI。这样就构成来跨进程通信的场景。 最开始的一节我们知道RemoteViews 是实现了Parcelable接口的,这样就可以跨进程使用了。从构造方法开始,系统首先根据包名去得到该应用的资源,然后inflate出布局文件,在SystemServer进程中是一个普通的view,而在我们的进程看来这是一个RemoteViews,然后会通过一系列set方法来更新该RemoteViews。

认识PendingIntent

所谓的 PendingIntent 是区别于 Intent 而存在的。Intent(即意图)是立即发生的,而 PendingIntent 是在将来的某个时刻发生的。PendIntent其实是Intent的封装。

PendingIntent的使用场景主要用于闹钟、通知、桌面部件。

与Intent的区别

  • Intent 是意图的意思。Android 中的 Intent 正是取自这个意思,它是一个消息对象,通过它,Android 系统的四大组件能够方便的通信,并且保证解耦。Intent 可以说明某种意图,携带一种行为和相应的数据,发送到目标组件。
  • PendingIntent是对Intent的封装,但它不是立刻执行某个行为,而是满足某些条件或触发某些事件后才执行指定的行为。

我们的 Activity 如果设置了 exported = false,其他应用如果使用 Intent 就访问不到这个 Activity,但是使用 PendingIntent 是可以的。

即:PendingIntent将某个动作的触发时机交给其他应用;让那个应用代表自己去执行那个动作(权限都给他)

获取PendingIntent

关于PendingIntent的实例获取一般有以下五种方法,分别对应Activity、Broadcast、Service:

  • getActivity()
  • getActivities()
  • getBroadcast()
  • getService()
  • getForegroundService()

它们的参数都相同,都是四个:Context, requestCode, Intent, flags,分别对应上下文对象、请求码、请求意图用以指明启动类及数据传递、关键标志位。前面三个参数共同标志一个行为的唯一性。

PendingIntent的FLAG

  • FLAG_CANCEL_CURRENT:如果当前系统中已经存在一个相同的PendingIntent对象,那么就将先将已有的PendingIntent取消,然后重新生成一个PendingIntent对象。
  • FLAG_NO_CREATE:如果当前系统中不存在相同的PendingIntent对象,系统将不会创建该PendingIntent对象而是直接返回null,如果之前设置过,这次就能获取到。
  • FLAG_ONE_SHOT:该PendingIntent只作用一次。在该PendingIntent对象通过send()方法触发过后,PendingIntent将自动调用cancel()进行销毁,那么如果你再调用send()方法的话,系统将会返回一个SendIntentException。
  • FLAG_UPDATE_CURRENT:如果系统中有一个和你描述的PendingIntent对等的PendingInent,那么系统将使用该PendingIntent对象,但是会使用新的Intent来更新之前PendingIntent中的Intent对象数据,例如更新Intent中的Extras

8.0通知栏新增通知渠道

Android 8.0 系统,Google引入通知渠道,提高用户体验,方便用户管理通知信息,同时也提高了通知到达率

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

build.gradle中targetSdkVersion设置大于等于26。这时如果不对通知渠道适配,通知就无法显示。

所以我们要额外处理:

1.创建NotificationChannel对象,指定Channel的id、name和通知的重要程度

2.使用NotificationMannager的createNotificationChannel方法来添加Channel。

    private NotificationCompat.Builder getNotificationBuilder() {        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {            NotificationChannel channel = new NotificationChannel("channel_id", "channel_name",                    NotificationManager.IMPORTANCE_DEFAULT);            //是否绕过请勿打扰模式            channel.canBypassDnd();            //闪光灯            channel.enableLights(true);            //锁屏显示通知            channel.setLockscreenVisibility(VISIBILITY_SECRET);            //闪关灯的灯光颜色            channel.setLightColor(Color.RED);            //桌面launcher的消息角标            channel.canShowBadge();            //是否允许震动            channel.enableVibration(true);            //获取系统通知响铃声音的配置            channel.getAudioAttributes();            //获取通知取到组            channel.getGroup();            //设置可绕过  请勿打扰模式            channel.setBypassDnd(true);            //设置震动模式            channel.setVibrationPattern(new long[]{100, 100, 200});            //是否会有灯光            channel.shouldShowLights();            getNotificationManager().createNotificationChannel(channel);        }        NotificationCompat.Builder notification = new NotificationCompat.Builder(this, "channel_id");        notification.setContentTitle("新消息来了");        notification.setContentText("周末到了,不用上班了");        notification.setSmallIcon(R.mipmap.ic_launcher);        notification.setAutoCancel(true);        return notification;    }

3.设置通知重要性级别

Android 8.0 及以上是使用NotificationManager.IMPORTANCE_,Android 7.1 及以下是使用NotificationCompat.PRIORITY_它们都是定义的常量:

总结

以上是我对通知栏相关使用或自定义方式的总结,这块也很简单,重点关注是RemoteViews和PendingIntent的知识点的认识和理解。

更多相关文章

  1. Android(安卓)语言切换
  2. 设置r.style
  3. Android基础笔记(十)- 帧动画、补间动画详解、对话框
  4. 设置文本边框 与 边框的样式:
  5. android网络与通信(三种网络接口简述 )
  6. ScrollView隐藏、调整大小
  7. android动态控制组件的位置、大小和新的动画
  8. Android(安卓)实用的 Linux命令
  9. Android(安卓)开发之通用的 PopupWindow

随机推荐

  1. 让Qt应用程序移植到Android上
  2. Android 网络编程之网络通信几种方式实例
  3. Android开发指南-三维图形
  4. Edittext禁止输入回车键以及单行显示解决
  5. android Linearlayout gravite 和layout_
  6. android ViewPager TabLayout 动态创建问
  7. android:ellipsize属性的含义
  8. Android-sharedUserId数据权限 android:s
  9. 一个用于Android的Web服务器
  10. [置顶] android通过服务实现消息推送