在安卓5.0棒棒糖系统中,通知栏图标变成了白色。

214

我有一个应用程序显示自定义通知。问题是在 Android 5 上运行时,通知栏中的小图标显示为白色。如何解决这个问题?


Rahul Sharma和basiam在这里有更好的答案 - https://dev59.com/DV0a5IYBdhLWcg3wCEww#33608653 - Rivu Chakraborty
这种情况发生在我运行Android 6时,而不是5。 - Jaime Montoya
当然,这种情况发生在Android 5+上,当然也包括Android 6和7。在提问时,Android 5是最新版本,并且引入了导致此问题的功能。 - Alejandro Casanova
1
我认为更准确的说法是API Level 21(Android 5.0),API Level 22(Android 5.1),API Level 23(Android 6.0)等。术语“Android 5”是含糊不清的,因为您可能正在谈论Android 5.0或Android 5.1,并且当我测试通知以及API Level 22与API Level 23中图标显示方式时,我意识到它们的行为不同。 - Jaime Montoya
20个回答

272

接受的答案并不完全正确。确实,它可以使通知图标显示为彩色,但是这样做存在一个很大的缺点——将目标SDK设置为低于Android Lollipop!

如果您按照建议将目标SDK设置为20来解决白色图标问题,则您的应用程序将无法针对Android Lollipop进行优化,这意味着您无法使用Lollipop特定的功能。

查看http://developer.android.com/design/style/iconography.html,您将看到白色风格是Android Lollipop中通知的预期显示方式。

在Lollipop中,Google还建议您使用一种颜色来显示(白色)通知图标的背景-https://developer.android.com/about/versions/android-5.0-changes.html

因此,我认为更好的解决方案是向应用程序添加剪影图标,并在设备运行Android Lollipop时使用它。

例如:

Notification notification = new Notification.Builder(context)
            .setAutoCancel(true)
            .setContentTitle("My notification")
            .setContentText("Look, white in Lollipop, else color!")
            .setSmallIcon(getNotificationIcon())
            .build();

    return notification;

而在getNotificationIcon方法中:

private int getNotificationIcon() {
    boolean useWhiteIcon = (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP);
    return useWhiteIcon ? R.drawable.icon_silhouette : R.drawable.ic_launcher;
}

40
你如何生成轮廓?我是一个没有Photoshop技能的开发者,使用在线工具使背景透明仍会导致白色图标。 - Chaitanya
6
使用GIMP :) - ByteHamster
4
这个解决方案并没有回答为什么Android构建过程会修改一个完全正确的PNG文件,压缩它并去除透明通道的问题。 - philk
8
http://developer.android.com/design/style/iconography.html 导向 https://material.google.com/style/icons.html,当我在该页面按下 ctrl+f 并输入 notification 时,它找不到任何内容。 - user25
5
代替从代码应用平台版本修改器到可绘制文件夹 - drawable / drawable-v21 进行检查并放置适当的图标。 - Igor
显示剩余11条评论

72

我完全同意用户Daniel Saidi的观点。为了使通知图标有颜色,我写下了这个答案。

为此,您需要制作像剪影一样的图标,并在您想要添加颜色的地方将某些部分设置为透明。即,

enter image description here

您可以使用以下代码添加颜色:

.setColor(您的颜色资源在此处)

注意:setColor仅在Lollipop中可用,因此您需要检查OSVersion。

if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
    Notification notification = new Notification.Builder(context)
    ...
} else {
    // Lollipop specific setColor method goes here.
    Notification notification = new Notification.Builder(context)
    ...
    notification.setColor(your_color)
    ...            
}
你也可以使用Lollipop作为目标SDK来实现这一点。
有关NotificationIcon的所有说明都在Google开发者控制台通知指南中。
优选的通知图标大小为24x24dp。
mdpi    @ 24.00dp   = 24.00px
hdpi    @ 24.00dp   = 36.00px
xhdpi   @ 24.00dp   = 48.00px

还可以参考通知图标大小了解更多信息。


4
将背景色设置为通知.setColor(your_color_resource_here)是一个非常好的想法。 - TejaDroid
1
所以我已经尝试过这个了,当我下拉通知栏时,通知图标有着彩色的背景。但是,在托盘中,它仍然是一个白色的图标。 - Stealth Rabbi
1
这个解决方案是正确的。有一个具有透明背景的通知图标,通过剪影,图标不一定只能是黑色的。图标可以有颜色;只是Android L会将显示颜色转换为白色。以前的Android版本将按原样显示图标。 - Lakshay Dulani
我正在为低于Lollipop版本添加setColor,可以正常显示ic_launcher图标,但在6.0上显示为白色方块图标,请帮助我。 - Harsha
我已经在Kitkat(API 19)和IceCreamSandwich(API 15)上测试了setColor,在这两种情况下它都忽略了颜色但是没有崩溃。那么我可以安全地省略检查操作系统版本吗? - Maria

48

这是Android用来显示通知图标的代码:

// android_frameworks_base/packages/SystemUI/src/com/android/systemui/
//   statusbar/BaseStatusBar.java

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

因此,您需要将目标SDK版本设置为<21,这样图标就会保持彩色。这是一个丑陋的解决方法,但它确实实现了预期效果。无论如何,我强烈建议遵循Google的设计准则"通知图标必须完全是白色的。"

以下是您可以实现它的方式:

如果您使用Gradle / Android Studio构建应用程序,请使用build.gradle

defaultConfig {
    targetSdkVersion 20
}
否则(使用Eclipse等)请使用AndroidManifest.xml
<uses-sdk android:minSdkVersion="..." android:targetSdkVersion="20" />

有什么变通方法吗?或者我该怎么做才能正确显示那个或任何其他图标? - Alejandro Casanova
我已经编辑了我的答案,因为在阅读了Android源代码后,我发现了一个解决方法。 - ByteHamster
4
非常感谢您的帮助,但是能否请您提供一些“上下文”信息。我需要在哪里提供上下文?“entry”是什么意思?请注意,我的翻译不会改变原意,只会使语言更加通俗易懂。 - Alejandro Casanova
@meh:是的,他们决定白色图标更适合Material Design。不能保证我的答案在以后的Android版本中仍然有效,因此您应该考虑创建一个特殊的图标。 - ByteHamster
降低目标版本会影响应用程序吗? - Munib
显示剩余7条评论

26
为避免通知图标变白,使用"Silhouette"图标,即白色透明背景图片。您可以使用Irfanview来制作它们:
- 选择一张图片,在IrfanView中打开,按F12键进入绘画工具,如果需要清理图片(去除不必要的部分,平滑和抛光) - 图像/减少色深度为2(对于黑白图片) - 图像/负数(对于黑底白字的图片) - 图像/调整大小/重新采样为144 x 144(使用大小方法"调整大小"而不是"重新采样",否则图片会再次增加到24位色深(24 BPP)) - 文件/另存为PNG,勾选显示选项对话框,勾选保存透明颜色,单击保存,然后单击图像中的黑色以设置透明颜色。
Android似乎只使用drawable-xxhdpi图片分辨率(144 x 144),因此将生成的ic_notification.png文件复制到\AndroidStudio\Projects\...\app\src\main\res\drawable-xxhdpi. 在您的代码中使用.setSmallIcon(R.drawable.ic_notification),或者像Daniel Saidi上面建议的那样使用getNotificationIcon()。您也可以使用Roman Nurik的Android Asset Studio

我试图使用Photoshop转换我的图标,但失败了,但是作为IrfanView的粉丝,当我看到它时,我迅速尝试了一下,结果非常出色。谢谢! - Bruce

15

另一种选择是利用特定于版本的可绘制(mipmap)目录,为Lollipop及以上版本提供不同的图形。

在我的应用中,“v21”目录包含带有透明文本的图标,而其他目录包含非透明版本(适用于早于Lollipop的Android版本)。

文件系统

看起来应该像这样:

Android Studio

这样,您就不需要在代码中检查版本号,例如:

PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
            PendingIntent.FLAG_ONE_SHOT);

Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
        .setSmallIcon(R.mipmap.ic_notification)
        .setContentTitle(title)
        .setContentText(message)
        .setAutoCancel(true)
        .setSound(defaultSoundUri)
            .setContentIntent(pendingIntent);
同样地,如果您在GCM有效负载中使用"icon"属性,则可以在其中引用"ic_notification"(或任何您选择的名称)。有关更多信息,请参见https://developers.google.com/cloud-messaging/http-server-ref#notification-payload-support

3
这个方案看起来和使用起来都非常符合 Android 的风格。 - Sergey Galin
你是在说如果Android版本是棒棒糖,它会从mipmap-21文件夹中选择图标,如果低于棒棒糖版本,则会从drawable中选择吗? - Lakshay Dulani
@Ian,目录方法对于每个Android版本的资源是很好的,但是你如何在不涉及额外代码的情况下设置setColor呢? - Daniel
@Daniel 颜色也可以放在特定版本的目录中,例如 "values" 和 "values-v21"。 - Ian

13
现在,Android Studio提供了一个插件Image Asset,它将在所有所需的drawable文件夹中生成图标。 Image Asset Studio可以帮助您创建不同密度的各种类型的图标,并向您显示它们在项目中的确切位置。它包括调整图标和添加背景的工具,同时在预览窗格中显示结果,以便它们完全按照您的意图显示。这些工具可以大大简化图标设计和导入过程。
您可以通过单击“新建”>单击“Image Asset”选项来访问Image Asset,它将显示如下窗口:

enter image description here

enter image description here


12

根据 Android 设计指南,您必须为builder.setSmallIcon(R.drawable.some_notification_icon);使用剪影,但如果您仍想显示彩色图标作为通知图标,这里是针对棒棒糖及以上版本的技巧:使用以下代码。largeIcon 将充当主要通知图标,并且您还需要为 smallIcon 提供剪影,因为它会显示在 largeIcon 的右下角。

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
     builder.setColor(context.getResources().getColor(R.color.red));
     builder.setSmallIcon(R.drawable.some_notification_icon);
     builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher));
}

在棒棒糖版本之前,只需在构建器中使用.setSmallIcon(R.mipmap.ic_launcher)


8
我曾经遇到过同样的问题,这是因为我的应用通知图标不是平面的。在安卓版本Lollipop及以下版本中,您的应用通知图标应该是平面的,请勿使用带有阴影等的图标。
下面是代码,在所有安卓版本上均可以完美运行。
private void sendNotification(String msg) {

    NotificationManager mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);

    Intent intent = new Intent(this, CheckOutActivity.class);

    PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, 0);

    NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
            this).setSmallIcon(R.drawable.ic_notification)
            .setContentTitle(getString(R.string.app_name))
            .setStyle(new NotificationCompat.BigTextStyle().bigText(msg))
            .setContentText(msg).setLights(Color.GREEN, 300, 300)
            .setVibrate(new long[] { 100, 250 })
            .setDefaults(Notification.DEFAULT_SOUND).setAutoCancel(true);

    mBuilder.setContentIntent(contentIntent);
    mNotificationManager.notify(new Random().nextInt(), mBuilder.build());
}

错误的图标
这里输入图片描述

正确的图标

这里输入图片描述


2
你确切地在这里修复什么问题? - IgorGanapolsky
如何创建正确的图标?是否可以在线生成?有什么工具吗? - Jiks
@Jiks 是的,这里是链接 - Muhammad Sohaib Ayub

7

Alpha通道是Android用于通知图标的唯一数据:

  • alpha == 1: 像素显示为白色
  • alpha == 0: 像素显示为您在Notification.Builder#setColor(int)中选择的颜色

这在https://developer.android.com/about/versions/android-5.0-changes.html中有提到:

系统忽略动作图标和主要通知图标中的所有非Alpha通道。您应该假设这些图标仅包含Alpha通道。

几乎所有内置绘图都似乎适合用作Alpha图像,因此您可以使用以下内容之一:

Notification.Builder.setColor(Color.RED)
                    .setSmallIcon(android.R.drawable.star_on)

但我仍在寻找正式确认此事的API文档

在Android 22上进行了测试。


我已经在Kitkat(API 19)和IceCreamSandwich(API 15)上测试了setColor,在这两种情况下它都忽略了颜色但是没有崩溃。那么我可以安全地省略检查操作系统版本吗? - Maria
@JohnLee 我认为它不会崩溃,只是如果你不考虑它的话可能看起来不好 :-) - Ciro Santilli OurBigBook.com

3

从manifest.xml中移除android:targetSdkVersion="21"。它会起作用!这样做完全不会影响您的apk,这只是一个技巧,我使用它并在通知中发现了彩色图标。


10
用“手法”欺骗系统是个非常糟糕的想法!这不是解决方案! - sud007

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接