1 简介
通知栏应该属于Android最常用的功能之一。目前几乎你认识的APP都会见过它们弹出的通知栏。通知栏的使用其实并不难,但你有没有感觉到随着整个Android的迭代会出现很多种用法,新版本的通知栏API无法兼容老系统这就会是一个很头疼的问题。本篇博文将会通过回顾历史使用情况和通用代码情况以及自定义通知栏来解析通知栏的各种使用方法。
2 系统默认通知栏使用
2.1 Android 3.0以下的通知栏使用(API Level < 11)
private void showNotification() {
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, newIntent(this, Main2Activity.class), PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new Notification();
notification.icon = R.mipmap.ic_launcher;
notification.setLatestEventInfo(this, "标题", "内容", pendingIntent);
NotificationManager manager =(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(1, notification);
}
2.2 Android 3.0(含)~ 4.1通知栏使用(11 <= API Level < 16)
private void showNotification() {
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, newIntent(this, Main2Activity.class), PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new Notification.Builder(this)
.setContentTitle("标题")
.setContentText("内容")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.getNotification();
NotificationManagermanager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(1, notification);
}
2.3 Android 4.1(含) ~ 8.0通知栏使用(16 <= API Level < 26)
private void showNotification() {
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, newIntent(this, Main2Activity.class), PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new Notification.Builder(this)
.setContentTitle("标题")
.setContentText("内容")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.build();
NotificationManager manager =(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(1, notification);
}
2.4 Android 8.0及以上通知栏使用(API Level >= 26)
从Android 8.0系统开始,Google引入了通知渠道这个概念。通知渠道就是将通知分类,而且从此以后,通知优先级并不再针对单条使用setPriority来设置,而是在渠道的定义过针对渠道来设置的。用户也可以通过手机设置中自由地选择渠道的优先级,响铃、振动、渠道的开关。好比一个App分功能重要类消息以及广告推荐类消息,用户可以自由选择是否对不喜欢的渠道消息进行处理。
渠道一旦创建后就不能修改,所以我们开发者需要在产品上线前认真规划好渠道,以免后期造成不必要的影响。现在来看看Android8.0系统的通知栏使用代码:
private void showNotification() {
if (Build.VERSION.SDK_INT < 26) {
return;
}
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
String channelId = "channel_id";
String channelName = "通知消息";
NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(channel);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, Main2Activity.class), PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new Notification.Builder(this, channelId)
.setContentTitle("标题")
.setContentText("内容")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.build();
notificationManager.notify(1, notification);
}
代码中定义了两个方法:创建渠道和显示通知。从参数中可知,创建渠道方法createNotificationChannel接收渠道ID、渠道名称 以及 重要等级,而显示通知方法showNotification接收渠道ID作为参数。渠道ID可以随便定义,但要保证全局唯一,而渠道名称是用户可在手机通知栏设置中查看的,所以需要能够表达清楚这个渠道的用途。前面提到消息优先级已经修改为针对渠道设置,这里的重要等级就是过去的优先级,当然用户也可以手动更改某个渠道的重要等级。
虽然渠道在创建后就不再可以修改,但是官方还是提供了删除的方法。不过这功能并不推荐使用,因为Google为了防止应用程序随意地创建垃圾通知渠道然后又进行删除,所以会在通知设置界面显示历史所有被删除的通知渠道数量。删除渠道代码如:
private void deleteNotificationChannel(String channelId) {
if (Build.VERSION.SDK_INT < 26) {
return;
}
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.deleteNotificationChannel(channelId);
}
有时候,我们一类重要通知渠道会被用户不小心设置了不展示,这样便会使用户错过了重要的通知,所以还可以进行渠道通知展示开关的判断和进行跳转提醒用户再次手动将其打开,代码如:
private boolean getNotificationChannelSwitch(String channelId) {
if (Build.VERSION.SDK_INT < 26) {
return false;
}
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
NotificationChannel channel = notificationManager.getNotificationChannel(channelId);
return channel.getImportance() != NotificationManager.IMPORTANCE_NONE;
}
private void gotoSettingNotificationChannel(String channelId) {
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
intent.putExtra(Settings.EXTRA_CHANNEL_ID, channelId);
startActivity(intent);
}
2.5 通用的实例
可见官方几乎几个版本后便会对通知消息进行下手重构,虽然功能上是越来越人性化了,但这也是我们开发者头痛的事情,那么现在我们就来实现一下如何通用地写出一个创建通知栏的Demo(不考虑太老的3.0以下):
private void createNotification(Context context) {
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Notification.Builder builder;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Android 8 及以上处理通知优先级和灯光、振动、铃声 和 创建 Notification.Builder
String channelId = "channel_id";
String channelName = "通知消息";
NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT);
// channel.enableLights(true); // 是否有呼吸灯,默认随系统设置
// channel.setLightColor(Color.RED); // 呼吸灯颜色
// channel.enableVibration(true); // 是否振动,默认随系统设置
// channel.setVibrationPattern(new long[]{100, 200, 300}); // 振动频率
// channel.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT); // 设为null便是静音,传入有效的Uri便自定义铃声,默认随系统设置
notificationManager.createNotificationChannel(channel);
builder = new Notification.Builder(context, channelId); // 如果使用一个参数的构造函数,可以在后期使用 builder.setChannelId(channelId);
} else {
// Android 8 以下创建 Notification.Builder 和 处理通知优先级和灯光、振动、铃声
builder = new Notification.Builder(context);
// int defaultType = 0;
// defaultType |= Notification.DEFAULT_VIBRATE;
// defaultType |= Notification.DEFAULT_LIGHTS;
// defaultType |= Notification.DEFAULT_SOUND;
// builder.setPriority(Notification.PRIORITY_DEFAULT);
// builder.setDefaults(defaultType);
}
builder.setContentTitle("标题");
builder.setContentText("内容");
builder.setSmallIcon(R.mipmap.ic_launcher);
// 点击后执行相应组件
builder.setContentIntent(PendingIntent.getActivity(this, 0, new Intent(context, MainActivity2.class), PendingIntent.FLAG_UPDATE_CURRENT));
// 大图
// Bitmap bitmap = XXX;
// Notification.BigPictureStyle bigPictureStyle = new Notification.BigPictureStyle();
// bigPictureStyle.bigPicture(bitmap);
// builder.setStyle(bigPictureStyle);
Notification notification = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN ? builder.build() : builder.getNotification();
notificationManager.notify(1, notification);
}
当然,如果你在创建Notification.Builder时不想使用判断API Level适配代码,也可以使用Android在appcompat-v7或androidx.core库中提供了一个NotificationCompat类来处理新旧系统中的兼容方案:
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "channel_id ");
2.5.1 Notification.Builder常用方法介绍
方法名 | 说明 |
setContentTitle(CharSequence title) | 设置标题(必需) |
setContentText(CharSequence title) | 设置内容(必需) |
setSmallIcon(int icon) | 设置小图标(必需) |
setContentIntent(PendingIntent intent) | 设置点击意图 |
setDeleteIntent(PendingIntent intent) | 设置划走意图 |
setTicker(CharSequence tickerText) | 设置简要内容(刚出现时在状态栏上动画显示的文字) |
setContentInfo(CharSequence info) | 设置内容信息(一般出现在右边时间下方的信息) |
setLargeIcon(Bitmap icon) | 设置大图标(MIUI、Flyme等国内订制系统不起作用) |
setColor(int argb) | 设置小图标背景色(APILevel >= 21) |
setWhen(long when) | 设置通知时间,默认当前系统时间 |
setShowWhen(boolean show) | 设置是否显示通知时间(API Level >= 17) |
setAutoCancel(boolean autoCancel) | 设置是否点击后自动取消 |
setOngoing(boolean ongoing) | 设置是否是正在运行的任务通知(是否常驻) |
setPriority(int pri) | 设置该通知优先级,优先级越高通知出现的位置越往前,默认:Notification.PRIORITY_DEFAULT |
setNumber(number) | 设置应用桌面图标角标,在API Level >=26情况下,结合NotificationChannel的setShowBadge(true) 才生效 |
setVisibility(int visibility) | 设置可见状态,例如:Notification.VISIBILITY_PUBLIC屏幕锁定时可见 |
setOnlyAlertOnce(boolean onlyAlertOnce) | 设置如果是同一ID的替换通知时,只会在首次出现时存在铃声、振动等打扰用户 |
setDefaults(int defaults) | 设置通知(声音、振动和闪灯)效果,可多个组合使用,值如:Notification.DEFAULT_VIBRATE(默认震动)、Notification.DEFAULT_SOUND(默认声音)、Notification.DEFAULT_LIGHTS(默认三色灯)Notification.DEFAULT_ALL(默认以上3种全部),等 |
setSound(Uri sound) | 设置声音效果。如:setSound(Uri.withAppendedPath(Audio.Media.INTERNAL_CONTENT_URI,"1")) 或 setSound(Uri.parse("file:///sdcard/XX.mp3")) |
setVibrate(long[] pattern) | 设置单独振动效果。如setVibrate(new long[] {100, 250, 100, 500}),表示延迟100ms,然后振250ms,在延迟100ms,接着在振动500ms |
setLights(int argb, int onMs, int offMs) | 设置闪灯效果。其中参数argb表示灯光颜色、onMs 亮持续时间、offMs 暗的时间。具体是支持还要看设备硬件。注意:只有在设置了Notification的标志符flags为Notification.FLAG_SHOW_LIGHTS的时候,才支持闪灯提醒 setProgress(int max, int progress, booleanindeterminate) 设置通知栏带进度条(API Level >= 14)。其中参数max表示进度条最大值、progress表示当前进度、indeterminate从翻译来看是:是否不确定,其实可以理解为当正在显示前面两个值设置的正常进度时,此值为false,当要显示一种无法评估的进度但要一直等待的状态就应该为true。常见的情况有:下载APP时可看到下载进度,要为false;当下载完后要安装时,无法评论安装进度就为true |
2.5.2 NotificationManager管理通知
新建/更新通知
我们在发出通知时,最终会使用到方法:NotificationManager.notify()。notify()方法接收一个ID和Notification对象,ID就是本次通知栏的ID。如果执行两次notify()方法使用了相同的ID,则只会出现一条通知栏,后面弹出的通知栏会替换先弹出来的通知栏。我们一般使用这种情况来更新通知栏信息。例如,短信APP,通过增加未读消息计数并将每封短信的摘要添加到通知,这种“堆叠”通知就可能这样使用。
删除通知
除非发生以下情况之一,否则通知仍然可见:
用户单独或通过使用“全部清除”清除了该通知(如果通知可以清除)。
用户点击通知,且您在创建通知时调用了setAutoCancel()。
针对特定的通知 ID 调用了 NotificationManager.cancel()方法,只删传入ID对应的一条消息。
调用了 NotificationManager.cancelAll() 方法,该方法将删除之前发出的所有通知。
2.5.3 PendingIntent
PendingIntent表示处于一种待定、即将发生状态的Intent(意图)。PendingIntent是在将来某个不确定的时刻发生,而Intent是立刻发生。PendingIntent的典型使用场景是给通知栏添加单击事件。PendingIntent能过send和cancel方法来发送和取消特定的Intent。
注意:
使用 PendingIntent 时,禁止使用空 intent(Intent的Component与action都为空称之“双无”Intent),同时禁止使用隐式 Intent
说明:
1) 使用 PendingIntent 时,使用了空 Intent,会导致恶意用户劫持修改 Intent 的内 容。
2) 使用PendingIntent 时,PendingIntent 中包装的 intent 如果是隐式的 Intent,容易遭到劫持,导致信息泄露
原因:
当A设定一个原始Intent(base intent)并据此创建PendingIntent,并将其交给B时,B就可以以A的身份来执行A预设的操作(发送该原始Intent),并拥有A同样的权限与ID。因此,A应当小心设置这个原始Intent,务必具备显式的Component,防止权限泄露。
权限泄露的风险在于,B得到这个PendingIntent后,还可以对其原始Intent进行有限的修改,这样就可能以A的权限与ID来执行A未预料的操作。
B可以修改的数据可以分成两类:
1 action,category,data,clipdata,package这些都可以修改,只要原来为空,或者开发者设置了对应的标志。
2 但selector与component,不论原来是否为空,都必须由开发者设置显式的标志才能修改
建议:
使用Intent.setPackage、Intent.setComponent、Intent.setClassName、Intent.setClass四种方法中任一种明确指定目标组件名称。
2.5.4 效果
不同的Android版本和不同的国内手机厂商系统所显示出来的通知栏会不一样,我们将上面的代码加入一些跟界面相关的设置方法来看下出来的通知栏显示样式看看显示情况。代码:
builder.setContentTitle("标题");
builder.setContentText("内容");
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setContentIntent(pendingIntent);
builder.setTicker("简要内容");
builder.setContentInfo("内容信息");
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.aa));
builder.setColor(Color.parseColor("#ff0000"));}
<?xml version="1.0"encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="按钮"/>
</RelativeLayout>
展示效果
注意
第一个是在Android原生系统6.0、第二个是在MIUI8安卓5.0 和第三个是在VIVO2.0安卓4.4中显示出来的效果。这里要重点说几点:
1、setLargeIcon()方法设置大图标在MIUI中是不生效的,通知栏左边图片位置始终显示小图标;
2、原生系统中大图标是显示在左边图片位置,而小图标是以角标的形式显示在大图的右下方,而默认背景色是灰色(示例中是设置了红点);
3、Android从5.0系统开始,通知栏小图标(无论是状态栏上还是角标上)只使用alpha图层来进行绘制(即变白了);
4、在Android4.4中小图标是显示在右边 和 简要内容生效了
3 自定义的通知栏使用
自定义通知栏只要稍修改一下上面的代码,把setContentTitle()和setContentText()方法换成setContent()方法即可。
3.1 示例
private void showNotification() {
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, newIntent(this, Main2Activity.class), PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent pendingIntent2 = PendingIntent.getActivity(this, 0, newIntent(this, Main2Activity.class), PendingIntent.FLAG_UPDATE_CURRENT);
RemoteViewsremoteViews = new RemoteViews(getPackageName(), R.layout.layout_notification);
remoteViews.setTextViewText(R.id.title, "我是自定义标题");
remoteViews.setImageViewResource(R.id.icon, R.drawable.aa);
remoteViews.setOnClickPendingIntent(R.id.btn, pendingIntent2);
NotificationCompat.Builder builder = newNotificationCompat.Builder(this);
builder.setContent(remoteViews);
//builder.setContentTitle("标题");
// builder.setContentText("内容");
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setContentIntent(pendingIntent);
Notification notification = builder.build();
NotificationManager manager =(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(1, notification);
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_toRightOf="@+id/icon"
android:gravity="center"/>
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:textColor="@color/btn_txt"
android:background="@color/btn_bg"
android:text="我是按钮" />
</RelativeLayout>
3.2 展示效果
可以看出,三种系统样式上相差还不算很大,但原生系统中有一个致命的错误,就是title的字体颜色。示例中是没有去设置TextView的字体颜色,那是不是把TextView的字体颜色设置成黑色就可以了?当然不能这样做,因为那样的话,在像VIVO系统那样通知栏默认背景不是白色的情况下也是非常的难看的。我们来思考一下,如果可以获得到系统通知栏默认的文字样式,这样是不是就可以显示出跟系统默认通知栏一样的效果了?朝着这个思路,我们来看看实现的办法。
3.3 自适应通知栏样式
在Android5.0以下中提供了通知栏样式:TextAppearance.StatusBar.EventContent.XXX,而5.0以后的版本中,不知出于什么考虑又修改了新的通知栏样式:TextAppearance.Material.Notification.XXX(所以说通知栏版本迭代一直都是一件令开发者头痛的事情)。然而我们的程序想要同时适配这两种情况,得在资源文件夹中新建layout-v21文件夹,同时在里头拷贝一份跟原来layout里同名的layout_notification.xml文件。我们来看看修改后的XML文件:
layout\layout_notification.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_toRightOf="@+id/icon"
android:gravity="center"
android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"/>
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:textColor="@color/btn_txt"
android:background="@color/btn_bg"
android:text="我是按钮" />
</RelativeLayout>
layout-v21\layout_notification.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_toRightOf="@+id/icon"
android:gravity="center"
android:textAppearance="@android:style/TextAppearance.Material.Notification.Title"/>
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:textColor="@color/btn_txt"
android:background="@color/btn_bg"
android:text="我是按钮" />
</RelativeLayout>
3.4 展示效果
3.5 自适应通知栏样式方法二
在网上找到了一种大神的解决通知栏样式的方案,原理大概是:通过获取系统通知栏默认的标题颜色,然后比对此颜色来判断些色值是接近黑色还是白色,从而在弹出通知栏时设置以其样式相应的颜色。(参考自:http://www.jianshu.com/p/426d85f34561)代码:
public class NoficationBar {
private static final double COLOR_THRESHOLD = 180.0;
private String DUMMY_TITLE = "DUMMY_TITLE";
private int titleColor;
//判断是否Notification背景是否为黑色
public boolean isDarkNotificationBar(Context context) {
return !isColorSimilar(Color.BLACK, getNotificationTitleColor(context));
}
//获取Notification 标题的颜色
private int getNotificationTitleColor(Context context) {
int color = 0;
if (context instanceof AppCompatActivity) {
color = getNotificationColorCompat(context);
} else {
color = getNotificationColorInternal(context);
}
return color;
}
//判断颜色是否相似
public boolean isColorSimilar(int baseColor, int color) {
int simpleBaseColor = baseColor | 0xff000000;
int simpleColor = color | 0xff000000;
int baseRed = Color.red(simpleBaseColor) - Color.red(simpleColor);
int baseGreen = Color.green(simpleBaseColor) - Color.green(simpleColor);
int baseBlue = Color.blue(simpleBaseColor) - Color.blue(simpleColor);
double value = Math.sqrt(baseRed * baseRed + baseGreen * baseGreen + baseBlue * baseBlue);
return value < COLOR_THRESHOLD;
}
//获取标题颜色
private int getNotificationColorInternal(Context context) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setContentTitle(DUMMY_TITLE);
Notification notification = builder.build();
ViewGroup notificationRoot = (ViewGroup) notification.contentView.apply(context, new FrameLayout(context));
TextView title = (TextView) notificationRoot.findViewById(android.R.id.title);
if (title == null) { //如果ROM厂商更改了默认的id
iteratorView(notificationRoot, new Filter() {
@Override
public void filter(View view) {
if (view instanceof TextView) {
TextView textView = (TextView) view;
if (DUMMY_TITLE.equals(textView.getText().toString())) {
titleColor = textView.getCurrentTextColor();
}
}
}
});
return titleColor;
} else {
return title.getCurrentTextColor();
}
}
private int getNotificationColorCompat(Context context) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
Notification notification = builder.build();
int layoutId = notification.contentView.getLayoutId();
ViewGroup notificationRoot = (ViewGroup) LayoutInflater.from(context).inflate(layoutId, null);
TextView title = (TextView) notificationRoot.findViewById(android.R.id.title);
if (title == null) {
final List<TextView> textViews = new ArrayList<>();
iteratorView(notificationRoot, new Filter() {
@Override
public void filter(View view) {
if (view instanceof TextView) {
textViews.add((TextView) view);
}
}
});
float minTextSize = Integer.MIN_VALUE;
int index = 0;
for (int i = 0, j = textViews.size(); i < j; i++) {
float currentSize = textViews.get(i).getTextSize();
if (currentSize > minTextSize) {
minTextSize = currentSize;
index = i;
}
}
textViews.get(index).setText(DUMMY_TITLE);
return textViews.get(index).getCurrentTextColor();
} else {
return title.getCurrentTextColor();
}
}
private void iteratorView(View view, Filter filter) {
if (view == null || filter == null) {
return;
}
filter.filter(view);
if (view instanceof ViewGroup) {
ViewGroup container = (ViewGroup) view;
for (int i = 0, j = container.getChildCount(); i < j; i++) {
View child = container.getChildAt(i);
iteratorView(child, filter);
}
}
}
interface Filter {
void filter(View view);
}
}
有了此类后,我们来修改弹出通知栏的代码:
private void showNotification() {
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, Main2Activity.class), PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent pendingIntent2 = PendingIntent.getActivity(this, 0, new Intent(this, Main2Activity.class), PendingIntent.FLAG_UPDATE_CURRENT);
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.layout_notification);
remoteViews.setTextViewText(R.id.title, "我是自适应颜色的标题");
remoteViews.setImageViewResource(R.id.icon, R.drawable.aa);
remoteViews.setOnClickPendingIntent(R.id.btn, pendingIntent2);
// 新加代码---------------------------------------------------
NoficationBar noficationBar = new NoficationBar();
boolean isDark = noficationBar.isDarkNotificationBar(this);
if (isDark) {
remoteViews.setTextColor(R.id.title, Color.parseColor("#ffffff"));
} else {
remoteViews.setTextColor(R.id.title, Color.parseColor("#000000"));
}
// 新加代码---------------------------------------------------
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
// builder.setContentTitle("标题");
// builder.setContentText("内容");
builder.setContent(remoteViews);
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setContentIntent(pendingIntent);
Notification notification = builder.build();
NotificationManager manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(1, notification);
}
为了验证效果,记得把原来layout\layout_notification.xml和layout-v21\layout_notification.xml在TextView加入的textAppearance去除哦。
3.6 展示效果
3.7 结论
从这三台手机上验证可知,无论方法一还是方法二都是可行的。不过由于国内手机厂商太多,而且各家对Android订制得都是非常个性的,笔者手头上资源有限,故无论我们在用方法一还是方法二,在实际开发过程中都有可能出现某某手机不兼容般的不可预知的情况。在使用自定义通知栏时,大家可根据实际情况实际开发。