Android开发通知栏的那些事
对于通知栏的使用,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)
.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的知识点的认识和理解。
作者:Toper-C
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341