@TryLoveCatch
2022-04-28T09:17:58.000000Z
字数 11967
阅读 2886
Android知识体系
我会根据具体的功能来介绍不同的Notification。
只要是Notification相关的代码,都用V4包里面的NotificationCompat。
构建参数,使用NotificationCompat.Builder提供的各种方法。
创建通知,请调用 NotificationCompat.Builder.build(),它将返回Builder配置相关参数的 Notification 对象。
要发出通知,请通过调用 NotificationManager.notify() 将 Notification 对象传递给系统。
普通视图布局限制为 64 dp,扩展视图布局限制为 256 dp
默认的notification,如下图:

big style的notification,如下图:

setContentTitle() setBigContentTitle()setLargeIcon() bigLargeIcon()setContentText()setContentInfo() setNumber()setSmallIcon()setWhen()bigText() addLine()当没有setLargeIcon()时候,setSmallIcon()就表示位置2。
官方文档里面写到:
Notification 对象必须包含以下内容:
小图标,由 setSmallIcon() 设置
标题,由 setContentTitle() 设置
详细文本,由 setContentText() 设置
但实际上,我们一般只需要setSmallIcon()是必写的。因为大多数情况下,我们都是自定义view,会调用setContent()方法来设置RemoteViews,所以setContentTitle()和setContentText()就不要写了。
android 4.1之后提供的扩展通知,可展开,通俗就是大视图通知。
关于big style,我们需要注意:
setStyle()方式来设置。系统给我们提供了三种style,我们大致说一下,因为这是三个不是重点。
这三种风格,都会有两个共用方法:
setBigContentTitle():这个会在展开的时候,覆盖setContentitle()的设置。
setSummaryText():这个是在试图的最底部,增加一个文本,貌似没什么用。
大文字风格:显示一个大的文字块

bigText():该方法可以设置一大段文字,会在展开时,覆盖setContentText()。
android.support.v4.app.NotificationCompat.BigTextStyle style = new android.support.v4.app.NotificationCompat.BigTextStyle();style.setBigContentTitle("BigContentTitle").bigText("很长很长很长很长很长很长很长很长很长").setSummaryText("SummaryText");NotificationCompat.Builder builder = new NotificationCompat.Builder(this).setContentTitle("BigTextStyle").setContentText("BigTextStyle演示示例").setSmallIcon(R.mipmap.ic_launcher).setStyle(style).setAutoCancel(true).setDefaults(NotificationCompat.DEFAULT_ALL);Notification notification = builder.build();NotificationManager manger = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);manger.notify(TYPE_Inbox,notification);
这个和BigTextStyle基本类似,不同就是提供的Api,这个里面的文字块控制换行。例如,IM消息提醒,同一个id的多条信息,可以合并成inbox模式,然后扩展的时候,显示每条消息的内容。
addLine():该方法可以设置一行行的文字,可以调用很多次,会在展开时,覆盖setContentText()。
android.support.v4.app.NotificationCompat.InboxStyle style = new android.support.v4.app.NotificationCompat.InboxStyle();style.setBigContentTitle("BigContentTitle").addLine("第一行").addLine("第二行").addLine("第三行").addLine("第四行").addLine("第五行").setSummaryText("SummaryText");NotificationCompat.Builder builder = new NotificationCompat.Builder(this).setContentTitle("InboxStyle").setContentText("InboxStyle演示示例").setSmallIcon(R.mipmap.ic_launcher).setStyle(style).setAutoCancel(true).setDefaults(NotificationCompat.DEFAULT_ALL);Notification notification = builder.build();NotificationManager manger = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);manger.notify(TYPE_Inbox,notification);
展开后,会在下方显示一张图片。

bigPicture():需要一个bitmap对象 所以也不应该传过大的图 否则会oom
bigLargeIcon():会覆盖setLargeIcon()
android.support.v4.app.NotificationCompat.BigPictureStyle style = new android.support.v4.app.NotificationCompat.BigPictureStyle();style.setBigContentTitle("BigContentTitle").bigPicture(BitmapFactory.decodeResource(getResources(),R.drawable.small));.setSummaryText("SummaryText");NotificationCompat.Builder builder = new NotificationCompat.Builder(this).setContentTitle("BigPictureStyle").setContentText("BigPictureStyle演示示例").setSmallIcon(R.mipmap.ic_launcher).setStyle(style).setAutoCancel(true).setDefaults(NotificationCompat.DEFAULT_ALL);Notification notification = builder.build();NotificationManager manger = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);manger.notify(TYPE_Inbox,notification);
这个才是重点,毕竟我们还是自定义的需求比较多。
setContent():这个是默认notification的自定义view
notification.contentView:同上
notification.bigContentView:这个是big style的自定义view
关键代码:
if(android.os.Build.VERSION.SDK_INT >= 16) {notification.bigContentView = remoteViews;}
bigContentView这个属性,在NotificationCompat类里面是没有的,所以我们需要build出Notification之后来设置。
setContent()和notification.bigContentView是能够一起使用的,一个设置默认的view,一个设置big的view,所以我们可以给定两个布局。
不过,其实,也可以给一个大布局,这个大布局的64dip在默认的notification显示,其他的,在展开之后显示。
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="horizontal"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#ff0000"><ImageViewandroid:id="@+id/img"android:layout_width="64dp"android:layout_height="64dp"android:scaleType="centerCrop"android:layout_alignParentLeft="true"android:layout_centerVertical="true"/><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="64dp"android:background="#000000"android:layout_toRightOf="@id/img"android:layout_marginLeft="15dip"><TextViewandroid:id="@+id/title"android:layout_width="match_parent"android:layout_height="wrap_content"android:singleLine="true"android:textSize="12sp"android:text="校园实时热点"android:layout_alignParentLeft="true"android:layout_alignParentTop="true"android:layout_centerVertical="true"/><TextViewandroid:id="@+id/msg"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_below="@id/title"android:textSize="12sp"android:text="jfkdsajflkdsajlk"/></RelativeLayout><ImageViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:layout_below="@id/img"android:src="@drawable/push_intro"/></RelativeLayout>
如上面的这个布局,在没有展开的情况下,push_intro这个ImageView是不会显示的,只有在展开的情况下才会显示。
另外,因为big style只有在通知栏第一个的位置,才会默认显示展开的内容,所以,我们通过设置setPriority(NotificationCompat.PRIORITY_MAX)提升自己notification的优先级,尽量让其显示在第一个,展示我们的big view。
NotificationCompat.Builder builder = newNotificationCompat.Builder(context).setAutoCancel(true).setOngoing(false).setVisibility(Notification.VISIBILITY_PUBLIC).setPriority(NotificationCompat.PRIORITY_MAX).setSmallIcon(R.drawable.logo).setContent(remoteViews);Notification notification = builder.build();if(android.os.Build.VERSION.SDK_INT >= 16) {notification.bigContentView = remoteViews;}NotificationManager manager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);manager.notify(NOTICE_ID_TYPE_0, notification);
5.0(21) 以上
官网上,可能触发浮动通知的条件示例包括:
注意,handUp显示的view是我们notification默认样式的view,也就是调用setContent()或notification.contentView的view,这个就涉及到如果是big style的notification,并且采用了handUp的方式,并不会显示big view。
代码如下:
setFullScreenIntent(PendingIntent.getBroadcast(context, 0, fullIntent(),PendingIntent.FLAG_CANCEL_CURRENT), true)或者setPriority(NotificationCompat.PRIORITY_MAX)setDefaults(Notification.DEFAULT_ALL)
奶5手机:
setFullScreenIntent():显示悬浮窗,用户不操作不会消失。
setPriority()&&setDefaults():显示悬浮窗,几秒之后就会自动消失。
小米手机:
setFullScreenIntent():显示悬浮窗,几秒之后就会自动消失。
setPriority()&&setDefaults():显示悬浮窗,几秒之后就会自动消失。
注意,小米手机必须在设置里面的通知栏管理,打开该app的悬浮窗通知。
如下图所示:

在状态栏显示一个动态改变的icon,经过调研,这个效果,可以有三种实现方式:
1、100张图片
也就是说,让UI切0-99的100张图片,然后动态的去替换,听上去匪夷所思,其实,很多app都是这样实现的。
2、100个xml
就是针对1的优化,只需要提供0-9这10个数字的图片,然后提供外面圆边框的10-20张不同状态的图片,然后通过这些图片,自己写layer-list来实现。
3、ClipDrawable
针对2的优化,还是提供0-9的图片,针对图中的最右边的那些效果,只需要提供,一个白色底图和一个完全状态下的图(如全是蓝色的图),然后我们通过clip调用setLevel()来实现切割,具体可参考:用ClipDrawable实现音频录制麦克风讲话效果
经过实验,感觉3可能有问题,虽然提供了setSmallIcon(id, level)
这个API,这个针对LevelListDrawable,你可以通过如下来控制:
<?xml version="1.0" encoding="utf-8"?><level-list xmlns:android="http://schemas.android.com/apk/res/android" ><!-- 0到 20显示这个图片--><item android:drawable="@drawable/s1" android:minLevel="0" android:maxLevel="20"></item><!-- 21到 40显示这个图片--><item android:drawable="@drawable/s2" android:minLevel="21" android:maxLevel="40"></item><!-- 41到 60显示这个图片--><item android:drawable="@drawable/s3" android:minLevel="41" android:maxLevel="60"></item><!-- 61到100显示这个图片--><item android:drawable="@drawable/s4" android:minLevel="61" android:maxLevel="100"></item></level-list>
然后,在带面里面调用
LevelListDrawable levelListDrawable = (LevelListDrawable) levelImg.getDrawable();levelListDrawable.setLevel(2);
但是 我们如果要是那个用ClipDrawable,就可能是这样使用的:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" ><item android:id="@android:id/background" android:drawable="@drawable/icon_microphone_normal" /><item android:id="@android:id/progress" ><clip android:drawable="@drawable/icon_microphone_recoding" android:gravity="bottom" android:clipOrientation="vertical" /></item></layer-list>
我们会结合LayerDrawable来使用,而这个会影响到LevelListDrawable,所以貌似3是不行的。
在5.0以上的系统上发现,平常的自定义notification出来的icon,居然在状态栏上变成了纯白色的icon。为什么会这样呢?我们来看源码:
protected void applyColorsAndBackgrounds(StatusBarNotification sbn,NotificationData.Entry entry) {if (entry.expanded.getId() != com.android.internal.R.id.status_bar_latest_event_content) {// Using custom RemoteViewsif (entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD&& entry.targetSdk < Build.VERSION_CODES.LOLLIPOP) {entry.row.setShowingLegacyBackground(true);entry.legacy = true;}} else {// Using platform templatesfinal int color = sbn.getNotification().color;if (isMediaNotification(entry)) {entry.row.setTintColor(color == Notification.COLOR_DEFAULT? mContext.getResources().getColor(R.color.notification_material_background_media_default_color): color);}}if (entry.icon != null) {if (entry.targetSdk >= Build.VERSION_CODES.LOLLIPOP) {entry.icon.setColorFilter(mContext.getResources().getColor(android.R.color.white));} else {entry.icon.setColorFilter(null);}}}
关键代码:
if (entry.targetSdk >= Build.VERSION_CODES.LOLLIPOP) {entry.icon.setColorFilter(mContext.getResources().getColor(android.R.color.white));}
因为google在android5.0上面做了限制,为了统一系统风格。之后的状态栏icon只能够有白色和透明两个颜色出现。
5.0以上(不包含5.0),系统默认通知栏图标为系统启动图标,会自动将通知栏的图标(有色区域)全部填充为白色,为了去除白色图标,镂空背景即可:
所以5.0之后,icon必须为透明或白色。
但是,如果我们要显示如下图所示:

因为前面的源码里面,有一个判断:
if (entry.targetSdk >= Build.VERSION_CODES.LOLLIPOP) {entry.icon.setColorFilter(mContext.getResources().getColor(android.R.color.white));}
所以我们可以通过降低targetSdkVersion来实现,将targetSdkVersion属性设置为20即可。
4.1(16)之后的提供的方法,通知栏的优先级,从-2 ~ 2,优先级逐步提升,优先级越高,在通知栏排的越靠前,优先级高的会在notification在状态栏显示一个icon,优先级相同的,最近的显示在最上面。
有五个优先级别,如下:
5.0(21)之后提供的方法。
这个方法可以控制在锁屏上显示的通知中可见的详细级别。 如下:
5.0以上支持
Notification可以在底部添加一些扩展性的功能,让用户更加便捷的操作。

builder.addAction(R.drawable.ic_prev, "Previous",prevPendingIntent)builder.addAction(R.drawable.ic_pause, "Pause", pausePendingIntent) builder.addAction(R.drawable.ic_next, "Next", nextPendingIntent)
特别和heads-up结合,在Android官网文档就有一个经典的场景,如下:

但是,注意了,如果notification是自定义的View,就不行,会出现一些奇奇怪怪的问题,所以必须是默认的notification样式或者大视图布局
PendingIntent如果创建?
PendingIntent提供了很多方法来创建:
PendingIntent.getActivity():启动一个Activity的PendingIntent如果创建
PendingIntent.getService():启动一个Service的PendingIntent如果创建
PendingIntent.getBroadCast():启动一个BroadCast的PendingIntent如果创建
上面说的创建PendingIntent的三个方法,最后一个参数,有四种选择:
但是,如果使用FLAG_UPDATE_CURRENT时候,可能会遇到一些问题,点击通知栏后,系统没有响应,这是怎么回事呢?
原来使用FLAG_UPDATE_CURRENT这个参数后,系统不会重新创建新的PendingIntent,这样一来,如果你传递的Intent的 extra参数没有变化的话,那么系统就会认为你没有发送新的PendingIntent,这样就不会重新响应你的点击事件。怎么解决呢?
在PendingIntent.getActivity()这三个方法的第二个参数,我们只要为这个参数设置一个独一无二的标识,就可以解决这个问题了,大多数解决方法如下:
int requestCode = (int) SystemClock.uptimeMillis();
获取发布通知时的时间,将它作为requestCode。
所以,我大多数都是用FLAG_CANCEL_CURRENT。
notification中哪些地方会用到PendingIntent呢?
主要有三个地方:
NotificationCompatsetSmallIcon()必须设置contentView和bigContentView可同时使用setFullScreenIntent或者setPriority()&&setDefaults()setFullScreenIntent()在奶5上会一直显示,直到用户操作setPriority()设置显示位置addAction()对自定义view不起作用PendingIntent最好使用FLAG_CANCEL_CURRENT参考:Android官方文档-通知
Android Notification常见样式总结
Android之Notification篇
android 5.0以上通知栏、状态栏图标变成白色
Android中的13种Drawable小结 Part 3
Android通知栏微技巧,那些你所没关注过的小细节