Android 消息通知栏用法详解(二) 适配8.0
通知(Notification) 是Android 系统比较有特色的一个功能,当某个应用程序希望向用户发出一些提示信息的时,而该应用程序又处于后台,就可以借助通知来实现。比如微信弹窗。通过这篇文章,我们将学习到
- Notification 的基本用法
- Notification 声音、优先级、多文字和大图片的显示
- Notification 高级玩法
- 自定义 Notification 的布局
一、基本用法
通知可以在 广播、activity 或者 service 去创建,不过在 activity 创建得比较少,一般都是应用在后台了,才需要去弄,不过我们是demo,所以写哪都没关系。
创建通知的基本步骤如下:
- 使用getSystemService 拿到 NotificationManager 的管理类
- 使用NotificationCompat 设置标题、内容、图片等
- 最后调用 NotificationManager 的 notify() 调出通知栏
上面中,为什么要使用 NotificationCompat 呢,因为Android系统的每一个版本都会对通知这部分功能进行或多或少的修改,API 比较不稳定,所以这里使用 v4 的包去兼容,当然,如果你使用 androidx,也就没啥问题了。
所以,一个简单的通知实例如下 :
以下代码建议在8.0之下运行,8.0以上,请参考下一篇文章
首先创建 NotificationManager
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mBuilder = new NotificationCompat.Builder(this)
.setContentTitle("这是标题")
.setContentText("我是内容,我是demo")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher));
//通过 builder.build() 拿到 notification
mNotificationManager.notify(1, mBuilder.build());
解释如下:
- setContentTitle 为设置标题
- setContentText 为正文内容
- setWhen 为通知创建时间,以 ms 为单位
- setSmallIcon 通知的小图标,注意只能使用 纯 alpha 图层的图片进行设置
- setLargeIcon 通知栏的大图标
都设置完成之后,只要使用 nofity() 即可,其中,第一个参数为 id,后面用来取消通知的。所以随便填一个数字即可,效果如下。
可以看到,小图标是灰色的。当然了,google 要求是 alpha 的图层呢。
什么是 纯 alpha 的图层图片呢?通俗来讲,就是图片不要带颜色就可以了,当然UI设计师肯定知道,就是用 alpha 层去绘制图片吗,咱们也可以从 阿里巴巴的图片库下载,比如:
然后咱们设置一下
但是,发现默认还是灰色的,这里可以使用 setcolor 去设置:
.setColor(getResources().getColor(R.color.colorPrimary))
这里咱们就设置好了小图标了
1.1 设置点击事件
一般的通知栏都是可以点击的,但是我们点击了一下,发现并没有什么用。当然,我们都没设置点击事件。
notification 的点击事件,可以通过设置 setContentIntent 来实现。它需要传递一个 PendingIntent。从而实现点击之后的跳转意图。
PendingIntent 的用法也很简单,它可以通过 getActivity(),getBroadcast() 和 getService() 来获取不同的实例,一个简单获取 activity 的方法如下:
Intent intent = new Intent(this,SecondActivity.class);
PendingIntent pi = PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_CANCEL_CURRENT);
它的参数也比较简单,第一个为 context,第二个为 requestCode,一般用不到,intent 即你要跳转的意图。第四个参数有5种选择,分别如下:
- FLAG_CANCEL_CURRENT:如果当前系统中已经存在一个相同的 PendingIntent 对象,那么就将先将已有的 PendingIntent 取消,然后重新生成一个 PendingIntent 对象。
- FLAG_IMMUTABLE:创建的PendingIntent不可变,API23加入。
- FLAG_NO_CREATE:如果当前系统中不存在相同的 PendingIntent 对象,系统将不会创建该 PendingIntent 对象而是直接返回 null 。
- FLAG_ONE_SHOT:该 PendingIntent 只作用一次。
- FLAG_UPDATE_CURRENT:如果系统中已存在该 PendingIntent 对象,那么系统将保留该 PendingIntent 对象,但是会使用新的 Intent 来更新之前 PendingIntent 中的 Intent 对象数据,例如更新 Intent 中的 Extras。
一般第四个传输,传递 0 或者FLAG_CANCEL_CURRENT 都可以。
所以,我们的通知代码改一下:
mBuilder = new NotificationCompat.Builder(this)
.setContentTitle("这是标题")
.setContentText("我是内容,我是demo")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
.setContentIntent(pi);
//通过 builder.build() 拿到 notification
mNotificationManager.notify(1, mBuilder.build());
1.2 取消通知
点击之后,发现确实跳转了,但是通知栏还在。取消共有两种方式
- setAutoCancel(true) 在builder 那里,添加自动取消的功能
- mNotificationManager.cancel(1); 通过取消 id 取消,如果有 tag ,也可以使用 cancel(tag,id) 来取消
1.3 通知更新
更新通知,只需要重新 notify 相同的 id 即可,如果通知栏还在,则更新内容,如果不存在,则增加一个新的通知栏,如下,我们更新一下内容:
mBuilder.setContentText("我更新了内容");
mNotificationManager.notify(1,mBuilder.build());
二、通知栏效果
上面,咱们只是简单的实现了通知栏,但是一些好玩的效果,咱们并没有实现,这节,一起来看看通知栏的效果。
(请用真机测试)
2.1 声音
很多通知栏,都有声音的效果,同样,我们也可以设置。使用 setSound 即可实现,比如:
mBuilder = new NotificationCompat.Builder(this)
...
//这个看各自的手机的铃声的路径
.setSound(Uri.fromFile(new File("system/media/audio/notifications/Argon.ogg")))
.setAutoCancel(true);
除了自定义,还可以使用默认铃声:
.setDefaults(NotificationCompat.DEFAULT_SOUND)
2.2 震动
除了声音,还可以让手机震动,使用的是 setVibrate,它的参数为一个 long 的数组,以 ms 为单位,如果我们想让手机震动1s,延时一秒,再震动一次,当然,它需要向申请权限
<uses-permission android:name="android.permission.VIBRATE"/>
.setVibrate(new long[]{0,1000,1000,1000})
同理,也可以使用默认震动效果:
.setDefaults(NotificationCompat.DEFAULT_VIBRATE)
2.3 LED灯
现在手机都LED灯,在手机息屏或者未接来电灯,还是还有必要的。同样,可以使用 setLights() 来实现效果。它又三个参数,第一个是设置灯的颜色,第二个是灯亮的时间,第三个则是灯暗的时间。如下:
.setLights(Color.RED,1000,1000)
同理,用默认的
.setDefaults(NotificationCompat.DEFAULT_LIGHTS)
ps:在国内百花齐放的环境中,上面那玩意基本无效,只能看各自的厂商的说明文档去适配了。
如果你觉得,上面的设置太麻烦,还有一个方法,直接全部设置成默认的:
.setDefaults(NotificationCompat.DEFAULT_ALL)
三、Notification 的高级玩法
在实际应用中,我们常常需要配合一下实际场所来灵活应用通知栏。比如长文字,打图片、回复信息、或者跳转等。下面一起来学习。
3.1 优先级
优先级在 7.1 及以下有用,8.0 则使用 channel 来设置。
通知栏是可以设置优先级的,它有5个常亮可以选
- PRIORITY_DEFAULT 默认优先级
- PRIORITY_MIN 优先级最低,某个特定场合才显示
- PRIORITY_LOW 优先级较低,系统可能将这类通知缩小,或者改变它的显示位置,比如靠后的位置
- PRIORITY_HIGH 表示较高程度,系统可能对它进行方法,或者改变位置,比如靠前的位置
- PRIORITY_MAX 优先级最高,这类通知会让用户立刻看到
比如,我们设置了 PRIORITY_MAX ,直接回显示出通知栏的效果:
3.2 setStyle 展开式通知
很多时候,我们想显示一段比较长的文字,或者一个比较大的图片,使用默认的 setContentText 或者 setLargeIcon 是不行的,但是可以使用 setStyle ,setStyle 可以让通知栏变大来显示通知。
3.2.1 长文字
比如,显示一段长文字:
.setStyle(new NotificationCompat.BigTextStyle().bigText("我是内容,我是demo\n我不是测试\n 我是长文字啊"))
效果如下:
3.2.2 大图片
设置一张图片:
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.update_app_top_bg))
.setStyle(new NotificationCompat.BigPictureStyle()
.bigPicture(BitmapFactory.decodeResource(getResources(),
R.mipmap.update_app_top_bg)).bigLargeIcon(null)
)
3.3 添加按钮
一个通知最多可以提供三个操作按钮,然该用户可以快速响应,比如暂停,上一曲下一曲这些常用的按钮,这里,可以通过 addAction 去编写,并把 PendingIntent 关联进去,比如广播:
mBuilder = new NotificationCompat.Builder(this)
......
.addAction(R.mipmap.enter,"上一曲",pi)
.addAction(R.mipmap.enter,"开始",pi)
.addAction(R.mipmap.enter,"下一曲",pi);
效果如下:
3.4 添加回复操作
google 在 Android 7.0 (API 24) 中引入允许用户直接回复的的组件,使用 RemoeInput 设置好key,并把它交给 action,即可实现。先看效果
首先,创建 RemoteInput.Builder() 的实例,并设置好 key 值,这个后面在 intent 会拿到。
然后,创建 PendingIntent,为了方便实验,添加一个广播
最后,创建 Action,把 remoteInput 和 pendingIntent 穿进去,代码如下:
private NotificationCompat.Action getRemoveAction(){
//添加一个回复组件,
RemoteInput remoteInput = new RemoteInput.Builder(REPLY_KEY)
.build();
//添加一个 pendingIntent 的广播
PendingIntent replyPi = PendingIntent.getBroadcast(this,2,
new Intent("com.zhengsr.test"),PendingIntent.FLAG_UPDATE_CURRENT);
//构建 action
NotificationCompat.Action action = new NotificationCompat.Action.Builder(R.mipmap.enter,
"回复",replyPi).addRemoteInput(remoteInput).build();
return action;
}
然后直接添加 action 即可:
mBuilder = new NotificationCompat.Builder(this)
....
.addAction(getRemoveAction())
;
3.4.1 拿到回复内容
如果想要拿到用户的输入内容,则可以调用 RemoteInput.getResultsFromIntent(intent) ,即可拿到 Bundle 并拿到数据,如:
class MyBroadcastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = RemoteInput.getResultsFromIntent(intent);
if (bundle != null) {
String replay = bundle.getString(REPLY_KEY);
Log.d(TAG, "zsr onReceive: "+replay);
}
}
}
同样,也可以使用 NotificationCompat.MessagingStyle 去专门传递消息。具体参考官方资料。
3.5 显示进度条
通知栏还可以显示进度条,在 builder 中使用 setProgress。
setProgress(int max, int progress, boolean indeterminate)
参数都能看得明白。比如,当我们开始的进度条,可以设置为:
setProgress(100,0,false)
更新的话,直接改变 progress 即可,如果想要取消,则 setProgress(0,0,false)就可
3.6 显示紧急通知
在某些情况下,就算屏幕锁屏了,一些通知还是要显示的,比如电话,闹钟等。当然,通知栏也支持,根据设备的情况,会分两种情况
- 设备锁定 : 如果设备锁定了,则显示全屏的 Acitivyt,直接覆盖全屏
- 设备为锁定: 则还是以通知栏的形式出现
可以通过设置 setFullScreenIntent(PendingIntent intent ,boolean highPriority) 来设置,如:
mBuilder = new NotificationCompat.Builder(this)
.setContentTitle("紧急通知")
.setContentText("这是一条紧急通知")
.setWhen(System.currentTimeMillis())
..
.setFullScreenIntent(pi,true)
其中 pi 为你要跳转的 PendingIntent。除了默认的情况,还可以通过设置 setVisibility(int visibility) 来设置锁屏屏幕的范围,共有三种选择:
- VISIBILITY_PUBLIC 显示通知的完整内容。
- VISIBILITY_SECRET 不在锁定屏幕上显示该通知的任何部分。
- VISIBILITY_PRIVATE 显示基本信息,例如通知图标和内容标题,但隐藏通知的完整内容。你如,显示"您有三条新短信",但是发件人和内容不可见。
四、自定义布局
一般情况下,系统的通知,并不能满足我们的需求,且为了保证每个版本的样式统一。都会采用自定义布局的形式。
需要注意的是。在创建自定义布局的时候,我们需要提供两个 layout,一个收起的视图,它的高度上限为 64dp。一个是展开之后,它的上限为 256dp。
4.1 为内容区域创建自定义布局
一般的自定义布局中,我们可以借助通用模板去构建一个基本布局,即保留时间戳,通知图标等装饰,而自定义由标题和文本内容的区域。
这个时候,可以使用 NotificationCompat.DecoratedCustomViewStyle 设置到 style 中,一般步骤如下:
- 构建基本通知,使用NotificationCompat.Builder
- 调用 setStyle().想其传递 NotificationCompat.DecoratedCustomViewStyle 实例
- 将其自定义布局扩展为 RemoveteViews 实例
- 调用 setCustomContentView 设置收起的通知布局
- 调用 setCustomBigContentView 设置扩展的通知布局
如果为多媒体播放控件创建自定义通知。可以使用NotificationCompat.DecoratedMediaCustomViewStyle 类
例如:
RemoteViews cusRemoveView = new RemoteViews(getPackageName(),R.layout.cus_notify_small);
RemoteViews cusRemoveExpandView = new RemoteViews(getPackageName(),R.layout.cus_notify_large);
mBuilder = new NotificationCompat.Builder(this)
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setStyle(new NotificationCompat.DecoratedCustomViewStyle())
.setCustomContentView(cusRemoveView)
.setCustomBigContentView(cusRemoveExpandView);
其中,当我们在自定义布局时,通知的背景颜色可能因为版本而异,如果要跟随系统,在设置 TextView 控件时,可以使用 TextAppearance_Compat_Notification 样式,比如标题的TextAppearance_Compat_Notification_Title。 这样就能保证在不同版本中,textview 始终是正常显示的。比如 R.layout_cus_notify_small:
注意,目前layout只支持 LinearLayout、FrameLayout、RelativeLayout三种基本布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="64dp"
android:orientation="vertical"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/TextAppearance.Compat.Notification.Title"
android:text="我是标题"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/TextAppearance.Compat.Notification.Info"
android:text="我是内容"/>
</LinearLayout>
而,R.layout_cus_notify_large 中,给其中一个 TextView设置红色,不设置 style,看看效果:
避免在 RemoteViews 上设置背景颜色,可能会导致文办颜色无法读取的问题
4.2 创建完全的自定义通知背景
如果你不想用系统的时间戳,通知图标等。可以去掉 setStyle(),只添加 setCustomContentView 和 setCustomBigContentView 即可。
要支持低于 Android 4.1(API 级别 16)的 Android 版本,您还应调用 setContent(),向其传递同一 RemoteViews 对象。