场景
开发过程中,经常会使用状态栏来通知用户。且不同的应用,状态栏的样式也差异万千,需要自定义。今天就总结一下安卓中的通知栏(Notification)知识。
本文结构主要分为:
- Notification 通知的作用
- 通知栏常见类型
- Notification的基本用法
- 自定义通知栏
1. Notification 通知的作用
从JellyBean 以来,通知系统做了一次被引入Android 以来最重大的结构性和功能性的变动。
通知可以包含操作,用户可以在通知抽屉中直接做出回应
通知在大小和布局方面更加灵活,可以展开显示更多的信息
通知有了优先级高低的排列方式,而不仅仅是时间的排列
通知状态栏的作用
1.显示接收短消息以及即时信息等(短信 ,QQ,微信等)
2.显示客户端的推送消息 (广告,推荐消息等)
3.显示正在进行的事务 (播放器的显示,版本更新等)
2. Notification的基本用法
通知的基本用法还是很灵活的,可以在活动中创建,也可以在广播接收器中创建,还可以在服务中创建。一般在广播和服务中用的比较多,因为只有在程序进入后台的时候,我们才需要使用通知,活动中使用的比较少。
Notification视觉风格
Notification有两种视觉风格,一种是标准视图(Normal view)、一种是大视图(Big view)。标准视图在Android中各版本是通用的,但是对于大视图而言,仅支持Android4.1+的版本。
从官方文档了解到,一个标准视图显示的大小要保持在64dp高,宽度为屏幕标准。
标准视图的通知主体内容有一下几个:
1.通知标题。
2.大图标。
3.通知内容。
4.通知消息。
5.小图标。
6.通知的时间,一般为系统时间,也可以使用setWhen()设置。
而对于大视图(Big View)而言,它的细节区域只能显示256dp高度的内容,并且只对Android4.1+之后的设备才支持,它比标准视图不一样的地方,均需要使用setStyle()方法设定
- setStyle()传递一个NotificationCompat.Style对象,它是一个抽象类,Android为我们提供了三个实现类,用于显示不同的场景。分别是:
NotificationCompat.BigPictureStyle, 在细节部分显示一个256dp高度的位图。
NotificationCompat.BigTextStyle,在细节部分显示一个大的文本块。
NotificationCompat.InboxStyle,在细节部分显示一段行文本。
2.1 通知的基本布局
通知的基本布局包括:
- 发送通知的应用图标或者发送人的头像
- 通知标题和消息
- 时间戳
- 当主图标显示发送人头像时,在副图标位置显示应用图标
2.2 通知的扩展布局
通知的扩展布局显示消息的前面几行或者图片的预览,后面的信息折叠起来,这样用户就可以看到更多的信息。用户可以通过 pinch-zoom 或者双手指滑动来打开扩展布局。Android 为单条消息提供了两种扩展布局 (文字和图像) 供你开发应用时使用。
通知的扩展布局示意图
从 Jelly Bean 开始,Android 支持在通知底部显示附加操作。通过这些操作,用户可以对通知直接执行常见的任务,而不用打开应用。这样可以加快操作,配合上滑出消失操作,使用户的通知抽屉体验更加顺滑。
可以放入通知中的操作有以下特点:
- 对于该通知重要、常用和典型的操作
- 时间紧迫的
- 不会与相邻的操作重复的
不要放置:
- 模糊的
- 和点击通知得到的效果一样的操作,例如阅读或者打开
2.3 通知的优先级
从 Jelly Bean 开始,Android 为通知增加了优先级标志。这样你可以使重要的通知相对于其他通知,总是显示在第一个。请通过以下的表格仔细选择通知的优先级
通知的优先级I 通知的优先级II
2.4 创建通知的步骤
1.创建一个通知管理类NotificationManager,通过调用 getSystemService()的方法获得。getSystemService方法接收一个字符串参数用于确定获取系统的哪个服务,传入Context.NOTIFICATION_SERVICE
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- 使用Builder构造器创建Notification对象。这里使用 support-v4库中提供的NotificationCompat
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context);
或者这样写:
Notification notification = NotificationCompat.Builder(context).builder();
3.对Builder进行配置(写法和dialog差不多)
mBuilder.setContentTitle("通知栏标题") // 设置通知栏标题
.setContentText("通知栏显示内容") // 设置通知栏显示内容
.setContentIntent(getDefalutIntent(Notification.FLAG_AUTO_CANCEL)) //设置通知栏点击意图
.setNumber(number) // 设置通知集合的数量
.setTicker("测试通知来啦") // 通知首次出现在通知栏,带上升动画效果的
.setWhen(System.currentTimeMillis()) // 通知产生的时间,会在通知信息里显示,一般是系统获取到的时间
.setPriority(Notification.PRIORITY_DEFAULT) // 设置该通知优先级
.setAutoCancel(true) // 设置这个标志当用户单击面板就可以让通知将自动取消
.setOngoing(false) // ture,设置他为一个正在进行的通知。他们通常是用来表示一个后台任务,用户积极参与(如播放音乐)或以某种方式正在等待,因此占用设备(如一个文件下载,同步操作,主动网络连接)
.setDefaults(Notification.DEFAULT_VIBRATE) // 向通知添加声音、闪灯和振动效果的最简单、最一致的方式是使用当前的用户默认设置,使用defaults属性,可以组合
//Notification.DEFAULT_ALL Notification.DEFAULT_SOUND 添加声音 // requires VIBRATE permission
.setSmallIcon(R.drawable.ic_launcher); // 设置通知小ICON
4.设置通知栏PendingIntent(点击动作事件等都包含在这里)。PendingIntent 从名字上看和Intent 很像,它们都指明一个意图,都可以开启活动,开启服务和发送广播。但是Intent 倾向于立即执行某个动作,而PendingIntent 倾向于某个时机执行动作。
5.最后一步,发送通知请求
mNotificationManager.notify(notifyId, mBuilder.build());
notify()方法有两个参数,一个参数是id,要保证每个通知所指定的id都是不同的,第二个参数则是Notification对象。
显示一个通知:
mNotificationManager.notify(1, mBuilder.build());
2.5 通知的方法
1. 设置提醒的标志符号flags,添加声音,设置闪灯,震动等效果。
两种设置方法:
1). 实例化之后,在设置flags
Notification notification = mBuilder.build();
notification.flags = Notification.FLAG_AUTO_CANCEL;
2). 通过setContentIntent(PendingIntent intent)方法中的意图设置对应的flags
public PendingIntent getDefalutIntent(int flags){
PendingIntent pendingIntent= PendingIntent.getActivity(this, 1, new Intent(), flags);
return pendingIntent;
}
提醒标志符成员:
Notification.FLAG_SHOW_LIGHTS //三色灯提醒,在使用三色灯提醒时候必须加该标志符
Notification.FLAG_ONGOING_EVENT //发起正在运行事件(活动中)
Notification.FLAG_INSISTENT //让声音、振动无限循环,直到用户响应 (取消或者打开)
Notification.FLAG_ONLY_ALERT_ONCE //发起Notification后,铃声和震动均只执行一次
Notification.FLAG_AUTO_CANCEL //用户单击通知后自动消失
Notification.FLAG_NO_CLEAR //只有全部清除时,Notification才会清除 ,不清楚该通知(QQ的通知无法清除,就是用的这个)
Notification.FLAG_FOREGROUND_SERVICE //表示正在运行的服务
- setDefaults(int defaults) (NotificationCompat.Builder中的方法,用于提示)
功能:向通知添加声音、闪灯和振动效果的最简单、使用默认(defaults)属性,可以组合多个属性(和方法1中提示效果一样的)
对应属性:
Notification.DEFAULT_VIBRATE //添加默认震动提醒 需要 VIBRATE permission
Notification.DEFAULT_SOUND // 添加默认声音提醒
Notification.DEFAULT_LIGHTS// 添加默认三色灯提醒
Notification.DEFAULT_ALL// 添加默认以上3种全部提醒
- setVibrate(long[] pattern)
功能:设置震动方式
.setVibrate(new long[] {0,300,500,700}); // 实现效果:延迟0ms,然后振动300ms,在延迟500ms,接着在振动700ms。
写法二:
mBuilder.build().vibrate = new long[] {0,300,500,700};
小提示:
如果没有震动效果,检查是否添加了震动权限:
<!-- 振动器权限 -->
<uses-permission android:name="android.permission.VIBRATE" />
- setLights(intledARGB ,intledOnMS ,intledOffMS )
功能:android支持三色灯提醒,这个方法就是设置不同场景下的不同颜色的灯。
描述:其中ledARGB 表示灯光颜色、 ledOnMS 亮持续时间、ledOffMS 暗的时间。
注意:
1)只有在设置了标志符Flags为Notification.FLAG_SHOW_LIGHTS的时候,才支持三色灯提醒。
2)这边的颜色跟设备有关,不是所有的颜色都可以,要看具体设备。
.setLights(0xff0000ff, 300, 0)
同理,实现同样的效果:
Notification notify = mBuilder.build();
notify.flags = Notification.FLAG_SHOW_LIGHTS;
notify.ledARGB = 0xff0000ff;
notify.ledOnMS = 300;
notify.ledOffMS = 300;
如果希望使用默认的三色灯提醒,设置了方法(2)中默认为DEFAULT_LIGHTS即可。
- setSound(Uri sound)
功能:设置默认或则自定义的铃声,来提醒。
//获取默认铃声
.setDefaults(Notification.DEFAULT_SOUND)
//获取自定义铃声
.setSound(Uri.fromFile(new File("file:///sdcard/xx/xx.mp3")))
//获取Android多媒体库内的铃声
.setSound(Uri.withAppendedPath(Audio.Media.INTERNAL_CONTENT_URI, "5"))
小提示:
如果没有闪光灯、震动效果,检查是否添加了相应权限:
<!-- 闪光灯权限 -->
<uses-permission android:name="android.permission.FLASHLIGHT" />
<!-- 振动器权限 -->
<uses-permission android:name="android.permission.VIBRATE" />
- setPriority(int pri)
对应属性(作用看上图就可知道):
Notification.PRIORITY_DEFAULT
Notification.PRIORITY_HIGH
Notification.PRIORITY_LOW
Notification.PRIORITY_MAX
Notification.PRIORITY_MIN
通知的优先级I
- setOngoing(boolean ongoing)
功能:设置为ture,表示它为一个正在进行的通知。他们通常是用来表示一个后台任务,用户积极参与(如播放音乐)或以某种方式正在等待,因此占用设备(如一个文件下载,同步操作,主动网络连接) - setProgress(int max, int progress,boolean indeterminate)
属性:max:进度条最大数值 、progress:当前进度、indeterminate:表示进度是否不确定,true为不确定,如下第3幅图所示 ,false为确定下第1幅图所示
功能:设置带进度条的通知,可以在下载中使用
- 设置点击取消
点击通知消息之后,上面的图标还是没有消掉。
比发送最简单的通知,发送具有 Action 的通知多了创建 Intent 、 PendingIntent 和 setContentIntent() 这几步。
1)在buider配置的方法中添加
.setAutoCancel(true)
2 ) 在创建管理通知类NotificationManager的时候,调用cancel()方法
NotificationManager manager= (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.cancel(1);// 这里的这个1 ,是在创建通知的时候给每条通知指定的notifyId 。因此如果你想取消哪条id就传哪个值。
2.6 PendingIntent 的用法
PendingIntent的用法很简单。它主要提供了几个静态方法用于获取PendingIntent的实现,可以根据需求使用getActivity()方法,getBroadcast()方法,getService()方法,这几个方法里面的参数都是一样的。第一个参数是Context ,第二个参数一般用不到传个0就好,第三个参数是一个Intent的对象,通过它来构建PendingIntent的“意图”,第四个用于确定PendingIntent的行为.
2.6.1 PendingIntent的位标识符:
FLAG_ONE_SHOT 表示返回的PendingIntent仅能执行一次,执行完后自动取消
FLAG_NO_CREATE 表示如果描述的PendingIntent不存在,并不创建相应的PendingIntent,而是返回NULL
FLAG_CANCEL_CURRENT 表示相应的PendingIntent已经存在,则取消前者,然后创建新的PendingIntent,这个有利于数据保持为最新的,可以用于即时通信的通信场景
FLAG_UPDATE_CURRENT 表示更新的PendingIntent
2.6.2 在各种情况下情况下它还会根据各种情况触发效果:
contentIntent:在通知窗口区域,Notification被单击时的响应事件由该intent触发;
deleteIntent:当用户点击全部清除按钮时,响应该清除事件的Intent;
fullScreenIntent:响应紧急状态的全屏事件(例如来电事件),也就是说通知来的时候,跳过在通知区域点击通知这一步,直接执行fullScreenIntent代表的事件。
1). 点击通知栏跳转到指定的XXActivity中
Intent intent = new Intent(context,XXX.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
mBuilder.setContentIntent(pendingIntent)
小提示
Android通知栏点击无法启动Activity时,请检查该Activity是否添加了该属性: android:exported="true"
。例如:
<activity android:name=".BootActivity"
android:exported="true"/>
2). 在执行了清空全部的通知操作时候,可以设置以下方法来相应这个事件
Intent deleteIntent = new Intent();
deleteIntent.setClass(context, XXXReceiver.class);
deleteIntent.setAction(DELETE_ACTION);
notification.deleteIntent = PendingIntent.getBroadcast(context, 0, deleteIntent, 0);
3). 在响应紧急事件(如来电)时候,可以设置以下方法来相应这个事件
setFullScreenIntent(PendingIntent intent, boolean highPriority)
小提示
关于 setSmallIcon() 与 setLargeIcon()。
在 NotificationCompat.Builder 中有设置通知的大小图标的两个方法。这两个方法有什么区别呢?当 setSmallIcon() 与 setLargeIcon() 同时存在时, smallIcon 显示在通知的右下角, largeIcon 显示在左侧;当只设置 setSmallIcon() 时, smallIcon 显示在左侧。
2.7 更新 Notification
更新通知很简单,只需要再次发送相同 ID 的通知即可,如果之前的通知还未被取消,则会直接更新该通知相关的属性;如果之前的通知已经被取消,则会重新创建一个新通知。
更新通知跟发送通知使用相同的方式。详见上节:创建 Notification
2.8 取消 Notification
取消通知有如下 5 种方式:
点击通知栏的清除按钮,会清除所有可清除的通知
设置了 setAutoCancel() 或 FLAG_AUTO_CANCEL 的通知,点击该通知时会清除它
通过 NotificationManager 调用 cancel(int id) 方法清除指定 ID 的通知
通过 NotificationManager 调用 cancel(String tag, int id) 方法清除指定 TAG 和 ID 的通知
通过 NotificationManager 调用 cancelAll() 方法清除所有该应用之前发送的通知
注意:如果你是通过 NotificationManager.notify(String tag, int id, Notification notify) 方法创建的通知,那么只能通过 NotificationManager.cancel(String tag, int id) 方法才能清除对应的通知,调用NotificationManager.cancel(int id) 无效。
3. 创建自定义的通知栏
Notification的自定义布局是RemoteViews,和其他RemoteViews一样,在自定义视图布局文件中,仅支持FrameLayout、LinearLayout、RelativeLayout三种布局控件和AnalogClock、Chronometer、Button、ImageButton、ImageView、ProgressBar、TextView、ViewFlipper、ListView、GridView、StackView和AdapterViewFlipper这些显示控件,不支持这些类的子类或Android提供的其他控件。否则会引起ClassNotFoundException异常
效果图
自定义的通知栏效果图3.1 创建自定义的步骤
1). 创建自定义的视图
2). 获取远程视图对象
3). 设置PendingIntent(来响应各种事件)
4). 发起Notification
1.创建自定义的视图
<?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="horizontal"
android:background="#789"
android:id="@+id/containor">
<ImageView
android:id="@+id/iv_not_head_image"
android:layout_height="80dp"
android:layout_width="80dp"
android:layout_gravity="center_vertical"
android:background="#789"
android:padding="10dp"
android:src="@drawable/ic_xingming"
android:scaleType="fitCenter"
/>
<LinearLayout
android:layout_width="0dp"
android:layout_weight="2.5"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#0093fe">
<TextView
android:id="@+id/tv_top_title"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="top"
android:gravity="center"
android:textColor="#fff"
android:textSize="18sp"/>
<TextView
android:id="@+id/tv_middle_title"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#0a0"
android:text="middle"
android:gravity="center"
android:textColor="#fff"
android:textSize="15sp"/>
<TextView
android:id="@+id/tv_bottom_title"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#050"
android:text="bottom"
android:gravity="center"
android:textColor="#fff"
android:textSize="15sp"/>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_custom_button"
android:layout_width="0dp"
android:layout_height="match_parent"
android:orientation="horizontal"
android:layout_weight="2"
android:background="#334455">
<ImageView
android:id="@+id/iv_not_up"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:src="@drawable/icon_left"
android:scaleType="centerInside"
android:background="#123"
/>
<ImageView
android:id="@+id/iv_camera"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:src="@drawable/icon_camera"
android:scaleType="centerInside"
/>
<ImageView
android:id="@+id/iv_not_next"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:src="@drawable/icon_right"
android:scaleType="centerInside"
android:background="#567"/>
</LinearLayout>
</LinearLayout>
步骤2、3、4
2). 获取远程视图对象
3). 设置PendingIntent(来响应各种事件)
/**
* 自定义通知栏View
*/
private void showNotificationRemoteView() {
RemoteViews mRemoteViews= new RemoteViews(getPackageName(),R.layout.view_notification);
mRemoteViews.setImageViewResource(R.id.iv_not_head_image, R.drawable.ic_xingming);
mRemoteViews.setViewPadding(R.id.containor, 0, 0, 0, 0);
mRemoteViews.setTextViewText(R.id.tv_top_title,"top");
mRemoteViews.setTextViewText(R.id.tv_middle_title,"middle");
mRemoteViews.setTextViewText(R.id.tv_bottom_title,"bottom");
if( Build.VERSION.SDK_INT <= 9){
mRemoteViews.setViewVisibility(R.id.ll_custom_button, View.GONE);
}else{
mRemoteViews.setViewVisibility(R.id.ll_custom_button, View.VISIBLE);
}
//点击事件处理
Intent buttonIntent=new Intent(DataLibraryConstant.DATA_LIBRARY_CONSTANT_STATUS_KEY);
// 头像点击
buttonIntent.putExtra(DataLibraryConstant.DATA_LIBRARY_CONSTANT_STATUS_KEY, R.id.iv_not_head_image);
//这里加了广播,所以INTENT的必须用getBroadcast方法
PendingIntent intent_head_image = PendingIntent.getBroadcast(this, 1, buttonIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mRemoteViews.setOnClickPendingIntent(R.id.iv_not_head_image, intent_head_image);
// top
buttonIntent.putExtra(DataLibraryConstant.DATA_LIBRARY_CONSTANT_STATUS_KEY, R.id.tv_top_title);
PendingIntent intent_title = PendingIntent.getBroadcast(this, 2, buttonIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mRemoteViews.setOnClickPendingIntent(R.id.tv_top_title, intent_title);
// middle
buttonIntent.putExtra(DataLibraryConstant.DATA_LIBRARY_CONSTANT_STATUS_KEY, R.id.tv_middle_title);
PendingIntent intent_content = PendingIntent.getBroadcast(this, 3, buttonIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mRemoteViews.setOnClickPendingIntent(R.id.tv_middle_title, intent_content);
// bottom
buttonIntent.putExtra(DataLibraryConstant.DATA_LIBRARY_CONSTANT_STATUS_KEY, R.id.tv_bottom_title);
PendingIntent intent_left = PendingIntent.getBroadcast(this, 4, buttonIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mRemoteViews.setOnClickPendingIntent(R.id.tv_bottom_title, intent_left);
// left
buttonIntent.putExtra(DataLibraryConstant.DATA_LIBRARY_CONSTANT_STATUS_KEY, R.id.iv_not_up);
PendingIntent intent_play= PendingIntent.getBroadcast(this,5,buttonIntent,PendingIntent.FLAG_UPDATE_CURRENT);
mRemoteViews.setOnClickPendingIntent(R.id.iv_not_up, intent_play);
// camera
buttonIntent.putExtra(DataLibraryConstant.DATA_LIBRARY_CONSTANT_STATUS_KEY, R.id.iv_camera);
PendingIntent intent_camera= PendingIntent.getBroadcast(this,7,buttonIntent,PendingIntent.FLAG_UPDATE_CURRENT);
mRemoteViews.setOnClickPendingIntent(R.id.iv_camera, intent_camera);
// right
buttonIntent.putExtra(DataLibraryConstant.DATA_LIBRARY_CONSTANT_STATUS_KEY,R.id.iv_not_next);
PendingIntent intent_right= PendingIntent.getBroadcast(this,6,buttonIntent,PendingIntent.FLAG_UPDATE_CURRENT);
mRemoteViews.setOnClickPendingIntent(R.id.iv_not_next, intent_right);
NotificationManager manager= (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
NotificationCompat.Builder builder= new NotificationCompat.Builder(this);
builder.setContent(mRemoteViews);
builder.setWhen(System.currentTimeMillis());
builder.setPriority(NotificationCompat.PRIORITY_MAX);
builder.setOngoing(true);
builder.setSmallIcon(R.drawable.ic_icon_time);
builder.setAutoCancel(true);
manager.notify(2, builder.build());
}
更多了解,可关注公众号:人人懂编程
微信公众号:人人懂编程
网友评论