前言
最近刚好有时间研究了一下Android的通知栏,作为用户,每天都会收到不同应用的通知,对于应用来说,能更好的把通知传递给每个用户很重要,作为应用内宣传的一种方式。平时大概遇到的都是下面这种通知栏,但是Android随着版本的更迭,也有很多花样的通知栏。 QQ图片20180730195411.png各个版本的构造方式
api11之前的:
public static void createBeforeApi11(Context context) {
NotificationManager noteManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
Notification notification = new Notification(R.mipmap.ic_launcher, "这是api11之前的通知栏内容", System.currentTimeMillis());
notification.tickerText="api11之前的标题";
//方法已经废弃了
// notification.setLatestEventInfo(context, "api11之前的标题", "api11之前的内容");
noteManager.notify(1, notification);
}
其中有些方法已经废弃了
api介于11和16之间:
public static void createBewApi11To16(Context context) {
// API Level >= 11 (Android 3.0) && API Level < 16 (Android 4.1)
NotificationManager noteManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
Notification.Builder builder = new Notification.Builder(context)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("api位于11和16之间的标题")
.setContentText("api位于11和16之间的内容")
.setTicker("api位于11和16之间的Ticker")
.setWhen(System.currentTimeMillis());
Notification note = builder.getNotification(); // 调用getNotification()来生成Notification
noteManager.notify(1, note);
}
api4之后兼容版本:
public static void createCommonApiAfter4(Context context) {
// API Level >= 11 (Android 3.0) && API Level < 16 (Android 4.1)
NotificationManager noteManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
//API Level >= 4 (Android 1.6) && API Level < 16 (Android 4.1)
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("api4之后通用的标题")
.setContentText("api4之后通用的内容")
.setTicker("api4之后通用的Ticker")
.setWhen(System.currentTimeMillis());
Notification note = builder.getNotification(); //调用builder.getNotification()来生成Notification
noteManager.notify(1, note);
}
android8.0之后的:
Android 8.0之后通知栏又有所改变,新的重要特性:NotificationChannel,如果你需要发送属于某个自定义渠道的通知,你需要在发送通知前创建自定义通知渠道,示例如下:
//ChannelId为"1",ChannelName为"Channel1"
NotificationChannel channel = new NotificationChannel("1",
"Channel1", NotificationManager.IMPORTANCE_DEFAULT);
channel.enableLights(true); //是否在桌面icon右上角展示小红点
channel.setLightColor(Color.GREEN); //小红点颜色
channel.setShowBadge(true); //是否在久按桌面图标时显示此渠道的通知
notificationManager.createNotificationChannel(channel);
ChannelId为当前渠道的id,ChannelName为渠道名称,如果你的编译的targetSDK版本是高于26的,那你就需要加入这个了,否则应用跑在Android8.0之后的手机上会报异常。这个必须加,但是两个值如果没有特殊的渠道的话可以随便填写,暂无影响,其他地方与普通的写法都一样。
public static void createApi26(Context context) {
NotificationManager noteManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel(id, name, NotificationManager.IMPORTANCE_HIGH);
noteManager.createNotificationChannel(channel);
Notification.Builder builder = new Notification.Builder(context, id)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("api26之后的标题")
.setContentText("api26之后的内容")
.setTicker("api26之后的Ticker")
.setWhen(System.currentTimeMillis())
.setAutoCancel(true);
Notification notification = builder.build();
noteManager.notify(1, notification);
}
特殊样式
主要包括下列一些,也有模仿QQ的还有前台服务开启的通知栏
public static final int TYPE_NORMAL = 1;
public static final int TYPE_PROGRESS = 2;
public static final int TYPE_BIGTEXT = 3;
public static final int TYPE_BIGPICTURE = 4;
public static final int TYPE_INBOX = 5;
public static final int TYPE_BIGCONTENT = 6;
public static final int TYPE_HANGUP = 7;
public static final int TYPE_IMITATE_QQ = 8;
public static final int TYPE_FORGROUND_SERVICE = 9;
public static final int TYPE_CUSTOM = 10;
一些共有的属性,我就在标准的通知栏里面基本都写了,简单做一下介绍:
普通样式
public static void createNormalNotification(Context context) {
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
//Ticker是状态栏显示的提示
builder.setTicker("简单Notification");
//第一行内容 通常作为通知栏标题
builder.setContentTitle("标题");
//第二行内容 通常是通知正文
builder.setContentText("通知内容");
//第三行内容 通常是内容摘要什么的 在低版本机器上不一定显示
builder.setSubText("这里显示的是通知第三行内容!");
//ContentInfo 在通知的右侧 时间的下面 用来展示一些其他信息
//builder.setContentInfo("2");
//number设计用来显示同种通知的数量和ContentInfo的位置一样,如果设置了ContentInfo则number会被隐藏
builder.setNumber(2);
//可以点击通知栏的删除按钮删除
builder.setAutoCancel(true);
//系统状态栏显示的小图标
builder.setSmallIcon(R.mipmap.ic_launcher);
//下拉显示的大图标
builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), ic_launcher_round));
Intent intent = new Intent(context, DetailActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 1, intent, 0);
//点击跳转的intent
builder.setContentIntent(pIntent);
//通知默认的声音 震动 呼吸灯
builder.setDefaults(NotificationCompat.DEFAULT_ALL);
Notification notification = builder.build();
mNotificationManager.notify(TYPE_NORMAL, notification);
}
-
//Ticker是状态栏显示的提示 ,但是4.1版本之后这个已经没有意义了
builder.setTicker("简单Notification"); -
//第一行内容 通常作为通知栏标题
builder.setContentTitle("标题"); -
//第二行内容 通常是通知正文
builder.setContentText("通知内容"); -
//第三行内容 通常是内容摘要什么的 在低版本机器上不一定显示,可以看做事简介
builder.setSubText("这里显示的是通知第三行内容!"); -
//ContentInfo 在通知的右侧 时间的下面 用来展示一些其他信息,number设计用来显示同种通知的数量和ContentInfo的位置一样,如果设置了ContentInfo则number会被隐藏,但是高版本这两个队不会出现,还有一些手机厂商做了修改,(小米手机)
builder.setContentInfo("2");
builder.setNumber(2); -
//点击通知栏的时候通知栏是否消失
builder.setAutoCancel(true); -
//系统状态栏显示的小图标(高版本悬挂式通知)
builder.setSmallIcon(R.mipmap.ic_launcher); -
//下拉显示的大图标(下拉通知菜单栏里面显示的图标)
builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), ic_launcher_round)); -
//点击跳转的intent(也可以跳转到浏览器页面,设置一个intent:Intent intent=new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.jianshu.com/p/4afc5c214a34"));)
Intent intent = new Intent(context, DetailActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 1, intent, 0);
builder.setContentIntent(pIntent);
最主要的还是PendingIntent 得flag的值,最上面四个特有的,默认写0 就行:
/** @hide */
@IntDef(flag = true,
value = {
FLAG_ONE_SHOT,
FLAG_NO_CREATE,
FLAG_CANCEL_CURRENT,
FLAG_UPDATE_CURRENT,Intent.FILL_IN_ACTION, Intent.FILL_IN_DATA, Intent.FILL_IN_CATEGORIES, Intent.FILL_IN_COMPONENT, Intent.FILL_IN_PACKAGE, Intent.FILL_IN_SOURCE_BOUNDS, Intent.FILL_IN_SELECTOR, Intent.FILL_IN_CLIP_DATA })
FLAG_CANCEL_CURRENT:如果构建的PendingIntent已经存在,则取消前一个,重新构建一个。
FLAG_NO_CREATE:如果前一个PendingIntent已经不存在了,将不再构建它。
FLAG_ONE_SHOT:表明这里构建的PendingIntent只能使用一次。
FLAG_UPDATE_CURRENT:如果构建的PendingIntent已经存在,则替换它,常用。
- 设定提示响应
通知提示有三种方式:铃声、闪光灯、震动。对于这三个属性,NotificationCompat.Builder提供了三个方法设定:
setSound(Uri sound):设定一个铃声,用于在通知的时候响应。传递一个Uri的参数,格式为“file:///mnt/sdcard/Xxx.mp3”。
setLights(int argb, int onMs, int offMs):设定前置LED灯的闪烁速率,持续毫秒数,停顿毫秒数。
setVibrate(long[] pattern):设定震动的模式,以一个long数组保存毫秒级间隔的震动。
通常情况我们只需要设置默认的就行
setDefaults(int)方法设定默认响应参数,在Notification中,对它的参数使用常量定义了,我们只需使用即可:
DEFAULT_ALL:铃声、闪光、震动均系统默认。
DEFAULT_SOUND:系统默认铃声。
DEFAULT_VIBRATE:系统默认震动。
DEFAULT_LIGHTS:系统默认闪光。
如果是震动和闪光灯需要添加权限:
<uses-permission android:name="android.permission.FLASHLIGHT"/>---闪光灯权限
<uses-permission android:name="android.permission.VIBRATE"/>---振动器权限
进度样式
BQ6P3K_QNGDP(9{59K(WMAR.pngpublic static void createProgressNotification(Context context, int progress) {
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), ic_launcher_round));
//禁止用户点击删除按钮删除
builder.setAutoCancel(false);
//禁止滑动删除
builder.setOngoing(true);
//取消右上角的时间显示
builder.setShowWhen(false);
builder.setContentTitle("下载中..." + progress + "%");
builder.setProgress(100, progress, false);
builder.setOngoing(true);
builder.setShowWhen(false);
Intent intent = new Intent(context, DownloadService.class);
intent.putExtra("command", 1);
Notification notification = builder.build();
mNotificationManager.notify(TYPE_PROGRESS, notification);
}
折叠式的显示文本样式
Android4.1+之后出现几种折叠样式,均需要使用setStyle()方法设定
setStyle()传递一个NotificationCompat.Style对象,它是一个抽象类,Android为我们提供了三个实现类,用于显示不同的场景。分别是:
- NotificationCompat.BigPictureStyle, 在细节部分显示一个256dp高度的位图。
- NotificationCompat.BigTextStyle,在细节部分显示一个大的文本块。
- NotificationCompat.InboxStyle,在细节部分显示一段行文本。
展开前
before.png
展开后
after.png
public static void createBigTextNotification(Context context) {
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setContentTitle("BigTextStyle");
builder.setContentText("BigTextStyle演示示例");
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), ic_launcher_round));
android.support.v4.app.NotificationCompat.BigTextStyle style = new android.support.v4.app.NotificationCompat.BigTextStyle();
style.bigText("这里是点击通知后要显示的正文,可以换行可以显示很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长");
style.setBigContentTitle("点击后的标题");
//SummaryText没什么用 可以不设置
style.setSummaryText("末尾只一行的文字内容");
builder.setStyle(style);
builder.setAutoCancel(true);
Intent intent = new Intent(context, DetailActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 1, intent, 0);
builder.setContentIntent(pIntent);
builder.setDefaults(NotificationCompat.DEFAULT_ALL);
Notification notification = builder.build();
mNotificationManager.notify(TYPE_BIGTEXT, notification);
}
折叠式的显示大图样式
样式与上面相似,都是下拉显示
public static void createBigPictureNotification(Context context) {
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setContentTitle("BigPictureStyle");
builder.setContentText("BigPicture演示示例");
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setDefaults(NotificationCompat.DEFAULT_ALL);
builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher_round));
NotificationCompat.BigPictureStyle style = new NotificationCompat.BigPictureStyle();
style.setBigContentTitle("BigContentTitle");
style.setSummaryText("SummaryText");
style.bigPicture(BitmapFactory.decodeResource(context.getResources(), R.mipmap.fly_pig));
builder.setStyle(style);
builder.setAutoCancel(true);
Intent intent = new Intent(context, DetailActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 1, intent, 0);
//设置点击大图后跳转
builder.setContentIntent(pIntent);
Notification notification = builder.build();
mNotificationManager.notify(TYPE_BIGPICTURE, notification);
}
折叠式多行显示
public static void createInboxNotification(Context context) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setSmallIcon(R.mipmap.lol_logo);
builder.setContentTitle("LOL");//系统限制,可能不显示
builder.setContentText("有没有开黑的");//系统限制,可能不显示
builder.setDefaults(Notification.DEFAULT_ALL);
//添加宽视图
NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle();
style.setBigContentTitle("皮尔特沃夫");
//由手机屏幕像素决定显示多少
style.addLine("刀锋之影");
style.addLine("放逐之刃");
style.addLine("无双剑姬");
style.addLine("疾风剑豪");
style.addLine("影流之主");
style.addLine("武器大师");
style.addLine("诺克萨斯之手");
style.addLine("德玛西亚之力");
style.addLine("德玛西亚皇子");
style.addLine("寒冰射手");
style.setSummaryText("每个人都曾是坑");//添加概要
builder.setStyle(style);
builder.setDefaults(NotificationCompat.DEFAULT_ALL);
Notification n = builder.build();
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(TYPE_INBOX, n);
}
折叠式自定义布局(已经过时)
public static void createBigContentNotification(Context context) {
Notification foregroundNote;
RemoteViews bigView = new RemoteViews(context.getPackageName(),
R.layout.notification_layout_big);
Notification.Builder mNotifyBuilder = new Notification.Builder(context);
foregroundNote = mNotifyBuilder.setContentTitle("下拉标题")
.setContentText("下拉展开所有的布局")
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher))
.build();
foregroundNote.bigContentView = bigView;
NotificationManager mNotifyManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
mNotifyManager.notify(TYPE_BIGCONTENT, foregroundNote);
}
悬挂式通知
这个QQ和知乎上面都有,平时如果推到后台,未读消息会以悬挂式通知来通知用户,部分手机(小米手机)需要主动手动去开启悬挂式通知权限,这个悬挂式通知栏是Android5.0之后出的。
float.png
public static void createHangupNotification(Context context) {
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setContentTitle("悬挂式通知");
builder.setContentText("部分手机(小米)请在设置通知管理中开启消息横幅提醒权限");
builder.setDefaults(NotificationCompat.DEFAULT_ALL);
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher_round));
Intent intent = new Intent(context, DetailActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 1, intent, 0);
builder.setContentIntent(pIntent);
//这句是重点
builder.setFullScreenIntent(pIntent, true);
builder.setAutoCancel(true);
Notification notification = builder.build();
mNotificationManager.notify(TYPE_HANGUP, notification);
}
另一种悬浮式通知
这种类型目前好像只在QQ和微信上面见过,当网络连接异常的时候会弹出,这种主要使用了悬浮窗来实现的,悬浮窗需要权限,有些手机(小米手机)需要主动手动开启,需要注意的是Android8.0之后关于悬浮窗的类型也有所改变,这些在demo中已经写的很清楚。
float2.png
申请权限和开启悬浮窗
public static void createQQTopNotification(Context context) {
if (FloatPermissionManager.getInstance().checkPermission(context)) {
//开启悬挂
FloatViewManager.getInstance().startFloatWindow();
} else {
Toast.makeText(context, "权限授予失败,无法开启悬浮窗", Toast.LENGTH_SHORT).show();
FloatPermissionManager.getInstance().applyPermission(context);
}
}
创建悬浮窗
private void initWindow() {
mWindowManager = WindowUtil.getWindowManager(getContext().getApplicationContext());
mParams = new WindowManager.LayoutParams();
if (Build.VERSION.SDK_INT >= 26) {
mParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
mParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
}
// mParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; // 设置window type
// 设置图片格式,效果为背景透明
mParams.format = PixelFormat.TRANSLUCENT;
//沉浸式的
mParams.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_FULLSCREEN;
mParams.windowAnimations = R.style.FloatWindowAnimation;//设置动画
mParams.gravity = Gravity.CENTER_VERTICAL | Gravity.TOP; // 调整悬浮窗口至右下角
// 设置悬浮窗口长宽数据
mParams.width = mWindowManager.getDefaultDisplay().getWidth();
mParams.height = width / 4;
//偏移为0
mParams.x = 0;
mParams.y = 0;
}
前台服务类通知
这种主要用于长时间驻留后台的服务,通过通知栏可以操控后台服务,典型的就是我们平时用的音乐播放器。
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate:");
Intent intent = new Intent(this, DetailActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("前台服务")
.setContentText("前台服务开启通知")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher_round))
.setContentIntent(pi)
.build();
startForeground(1, notification);
}
自定义通知栏
这种比较综合,模仿音乐播放器的通知栏
public static void createMusicNotification(Context context, int command) {
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setContentTitle("自定义通知标题");
builder.setContentText("自定义通知栏示例");
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher_round));
builder.setAutoCancel(false);
builder.setOngoing(true);//代表是常驻的,主要是配合服务
builder.setShowWhen(false);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.notification_custom);
//这里要根据当前的音乐状态显示不同的图标
//如果音乐更新的话 ,需要动态的设置背景 我这图标找的不好
// if (command == MediaService.COMMAND_START) {
// remoteViews.setImageViewResource(R.id.btn1, R.mipmap.pause);
// } else if (command == MediaService.COMMAND_PAUSE) {
// remoteViews.setImageViewResource(R.id.btn1, R.mipmap.play);
// }
//getService(Context context, int requestCode, @NonNull Intent intent, @Flags int flags)
//不同控件的requestCode需要区分开 getActivity broadcoast同理
//暂停或者重新播放
Intent startOrPause = new Intent(context, MediaService.class);
if (command == MediaService.COMMAND_START) {
startOrPause.putExtra("command", MediaService.COMMAND_START);
} else if (command == MediaService.COMMAND_PAUSE) {
startOrPause.putExtra("command", MediaService.COMMAND_PAUSE);
}
PendingIntent startOrPauseP = PendingIntent.getService(context, MediaService.COMMAND_START, startOrPause, 0);
remoteViews.setOnClickPendingIntent(R.id.btn1, startOrPauseP);
//这里是下一首的command
Intent nextSong = new Intent(context, MediaService.class);
nextSong.putExtra("command", MediaService.COMMAND_NEXT);
PendingIntent nextSongP = PendingIntent.getService(context, MediaService.COMMAND_NEXT, nextSong, 0);
remoteViews.setOnClickPendingIntent(R.id.btn2, nextSongP);
//取消 关闭当前服务
Intent mediaCancle = new Intent(context, MediaService.class);
mediaCancle.putExtra("command", MediaService.COMMAND_CANCLE);
PendingIntent mediaCancleP = PendingIntent.getService(context, MediaService.COMMAND_CANCLE, mediaCancle, 0);
remoteViews.setOnClickPendingIntent(R.id.btn3, mediaCancleP);
builder.setContent(remoteViews);
Notification notification = builder.build();
mNotificationManager.notify(TYPE_CUSTOM, notification);
}
总结
通知栏也是相当重要的一块,首先就是要做好版本兼容性,生成的Notification最直接的就是你当前项目编译的targetsdk的版本,如果你版本是高于26(Android8.0),你就必须要在代码中添加NotificationChannel,如果你的targetsdk是高于21(Android5.0)的话,并且你的项目运行在Android5.0以上的手机上,都是可以显示悬挂通知的,如果你的targetsdk高于16的话(Android4.1),并且项目运行在Android4.0以上的手机上,Android4.1之后出现的特性,下拉的那几种都是可以显示的,否则不行,主要还是需要注重做版本兼容性处理。文章写的也比较匆忙,如果有疑问可以留言讨论。
网友评论