@TryLoveCatch
2022-04-28T17:17:58.000000Z
字数 11967
阅读 2488
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">
<ImageView
android:id="@+id/img"
android:layout_width="64dp"
android:layout_height="64dp"
android:scaleType="centerCrop"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="64dp"
android:background="#000000"
android:layout_toRightOf="@id/img"
android:layout_marginLeft="15dip"
>
<TextView
android: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"
/>
<TextView
android: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>
<ImageView
android: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 = new
NotificationCompat.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 RemoteViews
if (entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
&& entry.targetSdk < Build.VERSION_CODES.LOLLIPOP) {
entry.row.setShowingLegacyBackground(true);
entry.legacy = true;
}
} else {
// Using platform templates
final 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
参数没有变化的话,那么系统就会认为你没有发送新的PendingInten
t,这样就不会重新响应你的点击事件。怎么解决呢?
在PendingIntent.getActivity()
这三个方法的第二个参数,我们只要为这个参数设置一个独一无二的标识,就可以解决这个问题了,大多数解决方法如下:
int requestCode = (int) SystemClock.uptimeMillis();
获取发布通知时的时间,将它作为requestCode。
所以,我大多数都是用FLAG_CANCEL_CURRENT
。
notification中哪些地方会用到PendingIntent呢?
主要有三个地方:
NotificationCompat
setSmallIcon()
必须设置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通知栏微技巧,那些你所没关注过的小细节