在 Android 开发中,通知栏是一个比较常见的功能,我们来看看如何来实现一些简单的通知栏。
实现通知栏主要由两个类:Notification 和 NotificationManager,但是由于 API 版本的不同导致实现方式会有所不同,所以 Google 提供了 v4 包里面的 NotificationCompat 和 NotificationManagerCompat 来实现:
先看一个最简单的通知栏:
// 简单通知
private void setSimpleNoti(){
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
// 设置标题
builder.setContentTitle("简单通知");
// 设置内容
builder.setContentText("通知内容");
// 设置小图标,没有大图标时,小图标会放在大图标的位置
builder.setSmallIcon(R.mipmap.ic_launcher);
NotificationManagerCompat managerCompat = NotificationManagerCompat.from(this);
// tag 和 id 是为了用来区分 notification
managerCompat.notify("NOTIFICATION",0101,builder.build());
}
这是一个最简单的通知栏,如果你要使用通知栏,这三个是必须填写的,那么如果是一个普通的通知栏需要什么东西呢?
// 普通通知
private void setNormalNoti(){
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
// 设置标题
builder.setContentTitle("普通通知");
// 设置内容
builder.setContentText("通知内容");
builder.setSmallIcon(R.mipmap.ic_launcher);
// 设置大图标
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher));
// 设置通知灯光(LIGHTS)、铃声(SOUND)、震动(VIBRATE)、(ALL 表示都设置)
builder.setDefaults(Notification.DEFAULT_ALL);
// 灯光三个参数,颜色(argb)、亮时间(毫秒)、暗时间(毫秒),灯光与设备有关
builder.setLights(Color.RED,200,200);
// 铃声,传入铃声的 Uri(可以本地或网上)我这没有铃声就不传了
builder.setSound(Uri.parse(""));
// 震动,传入一个 long 型数组,表示 停、震、停、震 ... (毫秒)
builder.setVibrate(new long[]{0,200,200,200,200,200});
NotificationManagerCompat managerCompat = NotificationManagerCompat.from(this);
managerCompat.notify("NOTIFICATION",0102,builder.build());
}
那么我们怎么给通知栏设置点击事件呢?因为通知栏已经不在这个 Activity 里了,我们设置平时使用的 Intent 就不行了,这个时候 Android 给我们提供了一个 PendingIntent,来看一下怎么使用:
// 设置 pendingIntent
private PendingIntent setPendingIntent(String msg) {
// 普通的 Intent,可以传值
Intent intent = new Intent(NotificationActivity.this, NotificationActivity.class);
intent.putExtra("notification", msg);
// 获取 pendingIntent,传入四个参数,这些参数大家都能看懂,第四个是一些 Flag,表示你这个通知栏会怎么变化,一般使用这个
return PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
// 解析 Intent
private void parseIntent() {
String msg = getIntent().getStringExtra("notification");
if (msg != null) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
}
因为我们点击通知栏就打开这个 Activity,所以就在这个 Activity 解析 Intent 的值,接下来为两个通知栏设置 Intent:
// 简单通知栏设置 Intent
builder.setContentIntent(setPendingIntent("简单通知"));
// 普通通知栏设置 Intent
builder.setContentIntent(setPendingIntent("普通通知"));
看一下效果图:
不知道大家有没有发现一个问题,就是我们不管点击的普通通知栏还是简单通知栏,Toast 显示的都是普通通知,这是因为:系统在使用 PendingIntent 时,会先看你的 Intent 是否是同一个,如果是同一个就直接使用已经存在的 PendingIntent,所以我们需要给 Intent 设置过滤器,我们使用 intent.setAction() 就行:
intent.setAction("notification:"+msg);
我们再来看看效果图:
这样这个问题就不存在了。
如果我们想要点击通知栏后就让通知栏消失或者强制消失,调用这个方法就行:
// 通知栏点击后自动消失
builder.setAutoCancel(true);
// 强制消失,传入 tag 和 id
managerCompat.cancel("notification",0101);
上面我们看到,一般的通知栏是可以右滑删除的,那我们怎么让它右滑不能删除了:
// 后台运行,无法右滑删除
builder.setOngoing(true);
进度条
一般我们在下载文件时,还会用到通知栏来显示进度条:
// 进度条通知栏
private void setProgress(){
final NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentTitle("源码下载");
builder.setContentText("等待下载中...");
builder.setSmallIcon(R.mipmap.ic_launcher);
final NotificationManagerCompat managerCompat = NotificationManagerCompat.from(this);
managerCompat.notify("notification",0103,builder.build());
// 使用子线程,模拟下载
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
// 设置进度条,三个参数表示:进度条最大值、当前值、确定在干啥:false
builder.setProgress(100,i,false);
builder.setContentText("文件已下载:"+i+"%");
managerCompat.notify("notification",0103,builder.build());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
builder.setContentText("文件下载完成");
managerCompat.notify("notification",0103,builder.build());
}
}).start();
}
效果图:
一般来说,我们会在通知栏添加一些按钮来进行一些操作,比如我们试一下:
// 设置按钮
private void setButton(){
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentTitle("按钮测试");
builder.setContentText("点一下按钮");
builder.setSmallIcon(R.mipmap.ic_launcher);
// 添加按钮,这三个参数相信大家都能明白
builder.addAction(R.mipmap.ic_launcher,"按钮一",setPendingIntent("执行按钮1"));
builder.addAction(R.mipmap.ic_launcher,"按钮二",setPendingIntent("执行按钮2"));
NotificationManagerCompat managerCompat = NotificationManagerCompat.from(this);
managerCompat.notify("notification",0104,builder.build());
}
效果图:
Android 6.0 以后还推出了悬浮通知栏:
// 悬浮通知栏,HIGH 或者 MAX 都可以
builder.setDefaults(NotificationCompat.PRIORITY_HIGH);
builder.setPriority(NotificationCompat.PRIORITY_HIGH);
效果图:
然而,对于我们的需求来说,有时候系统自带的通知栏是不够我们使用的,怎么办呢?我们就可以自定义通知栏:
// 自定义通知栏
private void setCustomNoti(){
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
// 实例化 RemoteViews,传入包名和布局 id
RemoteViews views = new RemoteViews(getPackageName(),R.layout.notification_view);
// 改变布局的 Text 的属性,传入 id 和 属性,也可以设置颜色,字体大小等
views.setTextViewText(R.id.tv_custom_notification,"自定义通知栏");
// 改变布局的图片,有不同的方法,可以传入 id、bitmap 等
views.setImageViewResource(R.id.iv_custom_notification,R.mipmap.ic_launcher);
// 设置点击事件,传入 id 和 PendingIntent
views.setOnClickPendingIntent(R.id.btn_custom_notification_clean,setPendingIntent("自定义按钮"));
// 设置 View
builder.setContent(views);
builder.setSmallIcon(R.mipmap.ic_launcher);
NotificationManagerCompat managerCompat = NotificationManagerCompat.from(this);
managerCompat.notify("notification",0105,builder.build());
}
效果图:
注意:1. 自定义通知栏的 RemoteViews 的布局只支持一些简单控件和布局,FrameLayout、LinearLayout、RelativeLayout、AnalogClock、button、Chronometer、ImageButton、ImageView 、ProgressBar、TextView、ViewFlipper、ListView、GridView、StackView、AdapterViewFlipper、ViewStub。不知道有没有写对,大家可以查一查,千万不要放一些不支持的布局或控件,或报错的。
2. 上面设置的 PendingIntent,都是开启一个新的 Activity,也可以用来绑定服务等其它操作,大家不要被束缚了思想。
3. Notification 还支持一些 Style,如:长文本、媒体等,可以直接实例化一个 Style:
android.support.v4.media.app.NotificationCompat.MediaStyle style = new android.support.v4.media.app.NotificationCompat.MediaStyle();
NotificationCompat.BigTextStyle style1 = new NotificationCompat.BigTextStyle();
builder.setStyle(style);
这样就行,它会导致通知栏的一些布局有不一样的效果,大家可以试试,但是需要注意的是,这些 Style 需要不同的 API 版本支持,使用的时候需要小心,避免程序出错。
项目地址:源代码