对于通知栏的使用,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是一个可以跨进程显示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 是区别于 Intent 而存在的。Intent(即意图)是立即发生的,而 PendingIntent 是在将来的某个时刻发生的。PendIntent其实是Intent的封装。
PendingIntent的使用场景主要用于闹钟、通知、桌面部件。
我们的 Activity 如果设置了 exported = false,其他应用如果使用 Intent 就访问不到这个 Activity,但是使用 PendingIntent 是可以的。
即:PendingIntent将某个动作的触发时机交给其他应用;让那个应用代表自己去执行那个动作(权限都给他)
关于PendingIntent的实例获取一般有以下五种方法,分别对应Activity、Broadcast、Service:
它们的参数都相同,都是四个:Context, requestCode, Intent, flags,分别对应上下文对象、请求码、请求意图用以指明启动类及数据传递、关键标志位。前面三个参数共同标志一个行为的唯一性。
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的知识点的认识和理解。