[关闭]
@TryLoveCatch 2022-04-28T17:17:58.000000Z 字数 11967 阅读 2488

Android知识体系之Notification相关知识

Android知识体系


我会根据具体的功能来介绍不同的Notification。

前言

Notification构造

默认的notification,如下图:

big style的notification,如下图:

  1. 内容标题 setContentTitle() setBigContentTitle()
  2. 大图标 setLargeIcon() bigLargeIcon()
  3. 内容 setContentText()
  4. 内容附加信息 setContentInfo() setNumber()
  5. 小图标 setSmallIcon()
  6. 时间 setWhen()
  7. 展开后的内容 bigText() addLine()

当没有setLargeIcon()时候,setSmallIcon()就表示位置2。

必选项

官方文档里面写到:

Notification 对象必须包含以下内容:

小图标,由 setSmallIcon() 设置
标题,由 setContentTitle() 设置
详细文本,由 setContentText() 设置

但实际上,我们一般只需要setSmallIcon()是必写的。因为大多数情况下,我们都是自定义view,会调用setContent()方法来设置RemoteViews,所以setContentTitle()setContentText()就不要写了。

big style

android 4.1之后提供的扩展通知,可展开,通俗就是大视图通知。

关于big style,我们需要注意:

系统给我们提供了三种style,我们大致说一下,因为这是三个不是重点。

这三种风格,都会有两个共用方法:
setBigContentTitle():这个会在展开的时候,覆盖setContentitle()的设置。
setSummaryText():这个是在试图的最底部,增加一个文本,貌似没什么用。

BigTextStyle

大文字风格:显示一个大的文字块

bigText():该方法可以设置一大段文字,会在展开时,覆盖setContentText()

  1. android.support.v4.app.NotificationCompat.BigTextStyle style = new android.support.v4.app.NotificationCompat.BigTextStyle();
  2. style.setBigContentTitle("BigContentTitle")
  3. .bigText("很长很长很长很长很长很长很长很长很长")
  4. .setSummaryText("SummaryText");
  5. NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
  6. .setContentTitle("BigTextStyle")
  7. .setContentText("BigTextStyle演示示例")
  8. .setSmallIcon(R.mipmap.ic_launcher)
  9. .setStyle(style)
  10. .setAutoCancel(true)
  11. .setDefaults(NotificationCompat.DEFAULT_ALL);
  12. Notification notification = builder.build();
  13. NotificationManager manger = (NotificationManager)
  14. getSystemService(NOTIFICATION_SERVICE);
  15. manger.notify(TYPE_Inbox,notification);

InboxStyle

这个和BigTextStyle基本类似,不同就是提供的Api,这个里面的文字块控制换行。例如,IM消息提醒,同一个id的多条信息,可以合并成inbox模式,然后扩展的时候,显示每条消息的内容。


addLine():该方法可以设置一行行的文字,可以调用很多次,会在展开时,覆盖setContentText()

  1. android.support.v4.app.NotificationCompat.InboxStyle style = new android.support.v4.app.NotificationCompat.InboxStyle();
  2. style.setBigContentTitle("BigContentTitle")
  3. .addLine("第一行")
  4. .addLine("第二行")
  5. .addLine("第三行")
  6. .addLine("第四行")
  7. .addLine("第五行")
  8. .setSummaryText("SummaryText");
  9. NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
  10. .setContentTitle("InboxStyle")
  11. .setContentText("InboxStyle演示示例")
  12. .setSmallIcon(R.mipmap.ic_launcher)
  13. .setStyle(style)
  14. .setAutoCancel(true)
  15. .setDefaults(NotificationCompat.DEFAULT_ALL);
  16. Notification notification = builder.build();
  17. NotificationManager manger = (NotificationManager)
  18. getSystemService(NOTIFICATION_SERVICE);
  19. manger.notify(TYPE_Inbox,notification);

BigPictureStyle

展开后,会在下方显示一张图片。

bigPicture():需要一个bitmap对象 所以也不应该传过大的图 否则会oom
bigLargeIcon():会覆盖setLargeIcon()

  1. android.support.v4.app.NotificationCompat.BigPictureStyle style = new android.support.v4.app.NotificationCompat.BigPictureStyle();
  2. style.setBigContentTitle("BigContentTitle")
  3. .bigPicture(BitmapFactory.decodeResource(getResources(),
  4. R.drawable.small));
  5. .setSummaryText("SummaryText");
  6. NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
  7. .setContentTitle("BigPictureStyle")
  8. .setContentText("BigPictureStyle演示示例")
  9. .setSmallIcon(R.mipmap.ic_launcher)
  10. .setStyle(style)
  11. .setAutoCancel(true)
  12. .setDefaults(NotificationCompat.DEFAULT_ALL);
  13. Notification notification = builder.build();
  14. NotificationManager manger = (NotificationManager)
  15. getSystemService(NOTIFICATION_SERVICE);
  16. manger.notify(TYPE_Inbox,notification);

bigContentView

这个才是重点,毕竟我们还是自定义的需求比较多。

setContent():这个是默认notification的自定义view
notification.contentView:同上
notification.bigContentView:这个是big style的自定义view

关键代码:

  1. if(android.os.Build.VERSION.SDK_INT >= 16) {
  2. notification.bigContentView = remoteViews;
  3. }

bigContentView这个属性,在NotificationCompat类里面是没有的,所以我们需要build出Notification之后来设置。

setContent()notification.bigContentView是能够一起使用的,一个设置默认的view,一个设置big的view,所以我们可以给定两个布局。
不过,其实,也可以给一个大布局,这个大布局的64dip在默认的notification显示,其他的,在展开之后显示。

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:orientation="horizontal"
  4. android:layout_width="match_parent"
  5. android:layout_height="match_parent"
  6. android:background="#ff0000">
  7. <ImageView
  8. android:id="@+id/img"
  9. android:layout_width="64dp"
  10. android:layout_height="64dp"
  11. android:scaleType="centerCrop"
  12. android:layout_alignParentLeft="true"
  13. android:layout_centerVertical="true"
  14. />
  15. <RelativeLayout
  16. android:layout_width="match_parent"
  17. android:layout_height="64dp"
  18. android:background="#000000"
  19. android:layout_toRightOf="@id/img"
  20. android:layout_marginLeft="15dip"
  21. >
  22. <TextView
  23. android:id="@+id/title"
  24. android:layout_width="match_parent"
  25. android:layout_height="wrap_content"
  26. android:singleLine="true"
  27. android:textSize="12sp"
  28. android:text="校园实时热点"
  29. android:layout_alignParentLeft="true"
  30. android:layout_alignParentTop="true"
  31. android:layout_centerVertical="true"
  32. />
  33. <TextView
  34. android:id="@+id/msg"
  35. android:layout_width="match_parent"
  36. android:layout_height="wrap_content"
  37. android:layout_below="@id/title"
  38. android:textSize="12sp"
  39. android:text="jfkdsajflkdsajlk"
  40. />
  41. </RelativeLayout>
  42. <ImageView
  43. android:layout_width="match_parent"
  44. android:layout_height="match_parent"
  45. android:layout_below="@id/img"
  46. android:src="@drawable/push_intro"
  47. />
  48. </RelativeLayout>

如上面的这个布局,在没有展开的情况下,push_intro这个ImageView是不会显示的,只有在展开的情况下才会显示。

另外,因为big style只有在通知栏第一个的位置,才会默认显示展开的内容,所以,我们通过设置setPriority(NotificationCompat.PRIORITY_MAX)提升自己notification的优先级,尽量让其显示在第一个,展示我们的big view。

  1. NotificationCompat.Builder builder = new
  2. NotificationCompat.Builder(context)
  3. .setAutoCancel(true)
  4. .setOngoing(false)
  5. .setVisibility(Notification.VISIBILITY_PUBLIC)
  6. .setPriority(NotificationCompat.PRIORITY_MAX)
  7. .setSmallIcon(R.drawable.logo)
  8. .setContent(remoteViews);
  9. Notification notification = builder.build();
  10. if(android.os.Build.VERSION.SDK_INT >= 16) {
  11. notification.bigContentView = remoteViews;
  12. }
  13. NotificationManager manager = (NotificationManager)
  14. context.getSystemService(Context.NOTIFICATION_SERVICE);manager.notify(NOTICE_ID_TYPE_0, notification);

heads-up

5.0(21) 以上
官网上,可能触发浮动通知的条件示例包括:

注意,handUp显示的view是我们notification默认样式的view,也就是调用setContent()notification.contentView的view,这个就涉及到如果是big style的notification,并且采用了handUp的方式,并不会显示big view。

代码如下:

  1. setFullScreenIntent(PendingIntent.getBroadcast(
  2. context, 0, fullIntent(),
  3. PendingIntent.FLAG_CANCEL_CURRENT)
  4. , true)
  5. 或者
  6. setPriority(NotificationCompat.PRIORITY_MAX)
  7. setDefaults(Notification.DEFAULT_ALL)

测试一

奶5手机:
setFullScreenIntent():显示悬浮窗,用户不操作不会消失。
setPriority()&&setDefaults():显示悬浮窗,几秒之后就会自动消失。

测试二

小米手机:
setFullScreenIntent():显示悬浮窗,几秒之后就会自动消失。
setPriority()&&setDefaults():显示悬浮窗,几秒之后就会自动消失。
注意,小米手机必须在设置里面的通知栏管理,打开该app的悬浮窗通知。

自定义smallIcon

smallIcon式样

如下图所示:

在状态栏显示一个动态改变的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,你可以通过如下来控制:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <level-list xmlns:android="http://schemas.android.com/apk/res/android" >
  3. <!-- 0到 20显示这个图片-->
  4. <item android:drawable="@drawable/s1" android:minLevel="0" android:maxLevel="20"></item>
  5. <!-- 21到 40显示这个图片-->
  6. <item android:drawable="@drawable/s2" android:minLevel="21" android:maxLevel="40"></item>
  7. <!-- 41到 60显示这个图片-->
  8. <item android:drawable="@drawable/s3" android:minLevel="41" android:maxLevel="60"></item>
  9. <!-- 61到100显示这个图片-->
  10. <item android:drawable="@drawable/s4" android:minLevel="61" android:maxLevel="100"></item>
  11. </level-list>

然后,在带面里面调用

  1. LevelListDrawable levelListDrawable = (LevelListDrawable) levelImg.getDrawable();
  2. levelListDrawable.setLevel(2);

但是 我们如果要是那个用ClipDrawable,就可能是这样使用的:

  1. <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
  2. <item android:id="@android:id/background" android:drawable="@drawable/icon_microphone_normal" />
  3. <item android:id="@android:id/progress" >
  4. <clip android:drawable="@drawable/icon_microphone_recoding" android:gravity="bottom" android:clipOrientation="vertical" />
  5. </item>
  6. </layer-list>

我们会结合LayerDrawable来使用,而这个会影响到LevelListDrawable,所以貌似3是不行的。

smallIcon变白

在5.0以上的系统上发现,平常的自定义notification出来的icon,居然在状态栏上变成了纯白色的icon。为什么会这样呢?我们来看源码:

  1. protected void applyColorsAndBackgrounds(StatusBarNotification sbn,
  2. NotificationData.Entry entry) {
  3. if (entry.expanded.getId() != com.android.internal.R.id.status_bar_latest_event_content) {
  4. // Using custom RemoteViews
  5. if (entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
  6. && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP) {
  7. entry.row.setShowingLegacyBackground(true);
  8. entry.legacy = true;
  9. }
  10. } else {
  11. // Using platform templates
  12. final int color = sbn.getNotification().color;
  13. if (isMediaNotification(entry)) {
  14. entry.row.setTintColor(color == Notification.COLOR_DEFAULT
  15. ? mContext.getResources().getColor(
  16. R.color.notification_material_background_media_default_color)
  17. : color);
  18. }
  19. }
  20. if (entry.icon != null) {
  21. if (entry.targetSdk >= Build.VERSION_CODES.LOLLIPOP) {
  22. entry.icon.setColorFilter(mContext.getResources().getColor(android.R.color.white));
  23. } else {
  24. entry.icon.setColorFilter(null);
  25. }
  26. }
  27. }

关键代码:

  1. if (entry.targetSdk >= Build.VERSION_CODES.LOLLIPOP) {
  2. entry.icon.setColorFilter(mContext.getResources().getColor(android.R.color.white));
  3. }

因为google在android5.0上面做了限制,为了统一系统风格。之后的状态栏icon只能够有白色和透明两个颜色出现。
5.0以上(不包含5.0),系统默认通知栏图标为系统启动图标,会自动将通知栏的图标(有色区域)全部填充为白色,为了去除白色图标,镂空背景即可:
所以5.0之后,icon必须为透明或白色。

但是,如果我们要显示如下图所示:

因为前面的源码里面,有一个判断:

  1. if (entry.targetSdk >= Build.VERSION_CODES.LOLLIPOP) {
  2. entry.icon.setColorFilter(mContext.getResources().getColor(android.R.color.white));
  3. }

所以我们可以通过降低targetSdkVersion来实现,将targetSdkVersion属性设置为20即可。

拾遗

setPriority

4.1(16)之后的提供的方法,通知栏的优先级,从-2 ~ 2,优先级逐步提升,优先级越高,在通知栏排的越靠前,优先级高的会在notification在状态栏显示一个icon,优先级相同的,最近的显示在最上面。
有五个优先级别,如下:

setVisibility

5.0(21)之后提供的方法。
这个方法可以控制在锁屏上显示的通知中可见的详细级别。 如下:

addAction

5.0以上支持
Notification可以在底部添加一些扩展性的功能,让用户更加便捷的操作。

  1. builder.addAction(R.drawable.ic_prev, "Previous",prevPendingIntent)
  2. 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提供了很多方法来创建:

PendingIntent.getActivity():启动一个Activity的PendingIntent如果创建
PendingIntent.getService():启动一个Service的PendingIntent如果创建
PendingIntent.getBroadCast():启动一个BroadCast的PendingIntent如果创建

上面说的创建PendingIntent的三个方法,最后一个参数,有四种选择:

但是,如果使用FLAG_UPDATE_CURRENT时候,可能会遇到一些问题,点击通知栏后,系统没有响应,这是怎么回事呢?
原来使用FLAG_UPDATE_CURRENT这个参数后,系统不会重新创建新的PendingIntent,这样一来,如果你传递的Intentextra参数没有变化的话,那么系统就会认为你没有发送新的PendingIntent,这样就不会重新响应你的点击事件。怎么解决呢?
PendingIntent.getActivity()这三个方法的第二个参数,我们只要为这个参数设置一个独一无二的标识,就可以解决这个问题了,大多数解决方法如下:

  1. int requestCode = (int) SystemClock.uptimeMillis();

获取发布通知时的时间,将它作为requestCode。
所以,我大多数都是用FLAG_CANCEL_CURRENT

notification中哪些地方会用到PendingIntent呢?
主要有三个地方:

结论

  1. 普通视图布局限制为 64 dp,扩展视图布局限制为 256 dp
  2. 使用V4包里面的NotificationCompat
  3. setSmallIcon()必须设置
  4. android 4.1之后可以使用big style,当处于通知栏第一个的时候,才会展开
  5. contentViewbigContentView可同时使用
  6. heads-up条件setFullScreenIntent或者setPriority()&&setDefaults()
  7. setFullScreenIntent()在奶5上会一直显示,直到用户操作
  8. setPriority()设置显示位置
  9. addAction()对自定义view不起作用
  10. PendingIntent最好使用FLAG_CANCEL_CURRENT

代码下载

TryLoveCatch

参考:Android官方文档-通知
Android Notification常见样式总结
Android之Notification篇
android 5.0以上通知栏、状态栏图标变成白色
Android中的13种Drawable小结 Part 3
Android通知栏微技巧,那些你所没关注过的小细节

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注