android 5.0以上通知栏、状态栏图标变成白色

原文地址:http://blog.csdn.net/gjy211/article/details/52189692


在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);
            }
        }
    }

github:

 
因为google在android5.0上面做了限制,为了统一系统风格。之后的状态栏icon就不能够随便用一张色彩丰富的图片了,只能够有白色和透明两个颜色出现。
5.0以上(不包含5.0),系统默认通知栏图标为系统启动图标,会自动将通知栏的图标(有色区域)全部填充为白色,
像一个白色格子,这是Google 为了实现材料设计规范,特意为之。为了去除白色图标,镂空背景即可:
所以这之后的想要设置显示状态栏icon不为白色: 这个icon只要背景需要透明,只让内容块纯白色。
另类的解决办法:(不推荐)
可以通过降低targetSdkVersion的方法,来达到显示彩色icon,但是不支持降低targetSdkVersion的方法。
(就是把AndroidManifest.xml uses-sdk节点的targetSdkVersion属性设置为21以下(不含21))
 
但是为了兼容5.0以下还正常使用:
我们判断下就可以分别设置 setSmallIcon
// 5.0
        if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP){
            notificationBuilder.setSmallIcon(R.drawable.ic_aphla_logo);
        } else {
            notificationBuilder.setSmallIcon(R.drawable.ic_logo);
        }

setLargeIcon 正常使用,不需要特别处理。使用旧的icon图片即可。

因为smallicon已经是透明,或者白色了,这时可以设置:
notificationBuilder.setColor();

然后就有背景颜色在通知栏上了。

----------------------------------------------------------------------

近期做项目,在项目快上线的时候突然发现android 5.0系统的手机出现了“状态栏 图标白板,通知栏图标不全”的现象,

吓了一跳,又要加班了啊!!!
在网上搜了一下,找到这篇博文 http://www.cnblogs.com/avenwu/p/4180193.html 谢谢,
直接搞定。
但其中有个问题,就此提出来。里面提到的图片要求,有的满足要求还是不行,但是觉得上面的博文比较靠谱,后来跟设计部换了几套图片,终于成功了。
项目drawable--XXX中图片名字要规范(保证各个drawable下内容相同的图片,名字相同),不然可能会出现状态栏中的图标和通知栏中的图标不一致。

(1)图片要求,除去以上提的,最好白底
(2)如果开发中没有规定一定要用5.0的SDK(21,22版本),可以把版本向下调到SDK 19 版本(或21版)的,这个“我看行”
(3)使用新的Api试试(我没有试过,自己动手吧)
在此贴出上面博文的内容:

Android5.0通知变化浅析

目前在Android中通知的使用还是很常见的,为了做版本兼容,常用兼容包NotificationCompat.Builder和 Notification.Builder。

  • NotificationCompat.Builder位于v4扩展包内(version 4 Support Library)
  • Notification.Builder在Android 3.0 开始引入(API level 11).

最近在Android5.0设备上发现一个问题:通知图标突然变成了白色的方块而不是代码中设置的icon。

问题原因

细读开发者文档其实也可以发现一些线索,虽然笔者是直接查的源码发现的问题原因。http://developer.android.com/design/patterns/notifications.html 一文的Use distinct icons部分介绍了几点关于通知的建议,其中的有两点是建议开发者不要做的行为。

Don't
Place any additional alpha (dimming or fading) into your small icons and action icons; they can have anti-aliased edges, but because Android uses these icons as masks (that is, only the alpha channel is used), the image should generally be drawn at full opacity.

Don't
Use color to distinguish your app from others. Notification icons should only be a white-on-transparent background image.

简单的说就是5.0后Android官方建议不要为通知的图标添加任何额外的透明度,渐变色,不要企图用颜色将通知图标与其他应用,比如系统应用,应用的通知图标只能是在透明的背景上有白色的图案。
至于原因,文档并没有细说,只是提到5.0系统将会在底层处理图标,想知怎么处理的可以参考Android SDK API level 21后的Notificaiton源码,里面写的较详细。
Project Structure
Project Structure

结合文档提供的图片示例,应该可以理解。
如果不遵循建议那么有很大几率是会出上文提到问题的,为什么不是别然出问题呢?
这还依赖于代码编译的版本,根据尝试,目前api 21以后编译会出问题,20及以前的版本编译不会出问题。所以解决问题比较简单粗暴的方案是用20及更早的版本编译代码。但是要测底解决问题,还是得遵循文档指导,及从新设计通知的图标以符合要求。

源码分析

下面看一下到底21的Android源码里面做了什么操作会导致通知的图标统统变白色。
Notification.Java

private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
	//...
	if (mLargeIcon != null) {
         contentView.setImageViewBitmap(R.id.icon, mLargeIcon);
         processLargeLegacyIcon(mLargeIcon, contentView);
         contentView.setImageViewResource(R.id.right_icon, mSmallIcon);
         contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
         processSmallRightIcon(mSmallIcon, contentView);
     } else { // small icon at left
         contentView.setImageViewResource(R.id.icon, mSmallIcon);
         contentView.setViewVisibility(R.id.icon, View.VISIBLE);
         processSmallIconAsLarge(mSmallIcon, contentView);
    }
    //...
}
        /**
         * Recolor small icons when used in the R.id.right_icon slot.
         */
        private void processSmallRightIcon(int smallIconDrawableId,
                RemoteViews contentView) {
            if (!isLegacy() || mColorUtil.isGrayscaleIcon(mContext, smallIconDrawableId)) {
                contentView.setDrawableParameters(R.id.right_icon, false, -1,
                        0xFFFFFFFF,
                        PorterDuff.Mode.SRC_ATOP, -1);

                contentView.setInt(R.id.right_icon,
                        "setBackgroundResource",
                        R.drawable.notification_icon_legacy_bg);

                contentView.setDrawableParameters(
                        R.id.right_icon,
                        true,
                        -1,
                        resolveColor(),
                        PorterDuff.Mode.SRC_ATOP,
                        -1);
            }
        }

这里我截取了两段比较关键的代码,在用NotificationCompat.Builder实例化我们的通知后,最终需要将各种图标,参数配置,应用到通知视图上面。可以看到如果我们只设置smallIcon而不设置largeIcon也是可以的,此时直接将small作为大图标设置给左侧的id为R.id.icon的ImageView。要注意的事一般情况下都不可以不设置smallIcon,否则通知无法正常显示出来。
processSmallIconAsLarge方法里面负责将我们设置的smallIcon二次处理,也就是这里会改变我们最终看到的通知图标,包括顶部状态栏和下拉显示的小图标。

参考

  1. http://developer.android.com/design/patterns/notifications.html
  2. http://developer.android.com/guide/topics/ui/notifiers/notifications.html
--------------------------------------------------------------------------------------------

最近遇到一个问题,就是本来是彩色的图标,结果在5.x的设备商status bar上面notification icon的颜色是黑白的,而在4.x上面是彩色的。

在SO上面找到这篇文章,http://stackoverflow.com/questions/28387602/notification-bar-icon-turns-white-in-android-5-lollipop。大意就是这个是5.x(api 21)的新行为,如果AndroidManfifext.XML中的android:targetSdkVersion设置为21,那么在5.x的系统上,就是这种行为。如果一定要显示彩色,像4.x那样,就设置android:targetSdkVersion为老的api号,例如20。

正好研究了下targetSdkVersion生效的机制。对于这个例子而言,可以参看BaseStatusBar的实现代码:

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

他会根据entry.targetSdk判断是否使用黑白模式。

这里的targetSdk就是在android:targetSdkVersion。如果看这段代码的调用关系,就会发现entry.targetSdk的值是通过:

ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
entry.targetSdk = info.targetSdkVersion;

赋值的,即ApplicaitonInfo.targetSdkVersion。

所以如果是在5.x的系统(sdk version是21/22)上运行应用,如果指定了targetsdkversion为21/22,那就会生效黑白模式;如果指定了targetSdkVersion为20或更低的值,则这段新的原色过滤的逻辑不生效,还是使用原有系统的行为。于是如果我们想在新的系统上仍然让应用保持旧系统的行文,就可以通过设置该属性为旧api版本来实现。

如果设置了targetSdkVersion为21,但运行在旧的4.x系统上(例如api 19),那因为实际的sdk runtime根本没有处理这么高targetSdkVersion的代码,所以自然也不会生效。而且sdk的代码一般采用>=某个版本号的方式来判断,所以保证了写21和写19的效果在19的机器上是完全一样的。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android 5.0 及以上的版本中,由于安全性增强,无法直接通过包名启动或置顶应用程序。需要您先获取应用程序的启动 `Intent`,然后使用该 `Intent` 启动或置顶应用程序。 以下是示例代码,用于根据包名启动或置顶应用程序: ```java public void openOrTopApp(Context context, String packageName) { PackageManager pm = context.getPackageManager(); Intent launchIntent = pm.getLaunchIntentForPackage(packageName); if (launchIntent != null) { // 应用程序已经安装,启动或置顶应用程序 launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningTaskInfo> taskList = am.getRunningTasks(Integer.MAX_VALUE); boolean isAppRunning = false; for (ActivityManager.RunningTaskInfo task : taskList) { if (task.topActivity.getPackageName().equals(packageName)) { isAppRunning = true; break; } } if (isAppRunning) { // 应用程序已经在前台运行,将其置顶 launchIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); } context.startActivity(launchIntent); } else { // 应用程序未安装 Toast.makeText(context, "应用程序未安装", Toast.LENGTH_SHORT).show(); } } ``` 在这个示例中,我们首先使用包管理器 `PackageManager` 获取应用程序的启动 `Intent`,如果应用程序已经安装,我们就使用该 `Intent` 启动或置顶应用程序。 如果应用程序已经在前台运行,我们就将 `Intent` 添加 `FLAG_ACTIVITY_REORDER_TO_FRONT` 标志来将其置顶;否则,我们将 `Intent` 添加 `FLAG_ACTIVITY_NEW_TASK` 标志来启动应用程序。 请注意,由于 Android 5.0 及以上版本的安全性增强,如果您想要启动或置顶其他应用程序,您需要在应用程序清单文件中声明相应的权限。例如,如果您想要启动或置顶系统应用程序,您需要在应用程序清单文件中声明 `android.permission.INTERACT_ACROSS_USERS_FULL` 权限。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值