Android通知栏介绍与适配总结

因为历史缘由,Android在发布之初对通知栏Notification的设计至关简单,而现在面对各式各样的通知栏玩法,谷歌也不得不对其进行更新迭代调整,增长新功能的同时,也在不断地改变样式,试图迎合更多人的口味。本文总结了Android通知栏的版本迭代过程,在通知栏开发过程当中所遇到的各类各样的坑,以及一些解决技巧,特别的,对于大众期盼的Android 7.0的到来,通知栏又会发生怎样的改变呢?接下来一一进行介绍。html

Android通知栏发展历史
首先来看一张各个Android版本通知栏消息的全家福。java

Drawingandroid

(点击查看大图)api

Android通知栏从最初的Android1.1系统一直到现在的7.X版本,发生了翻天覆地的变化。从图中能够看出,1.X-2.2版本的通知栏采用了白色背景和黑色字体;2.3-4.X版本,默认背景变成了黑色,而主标题采用白色字体,内容为灰色字体。从Android5.0开始,又更改成白色背景和黑色字体。固然,这只是原生的Android系统通知栏默认颜色,许多厂商对每一个Android的版本都尝试了各式各样的修改,在此不一一介绍。缓存

下面分别介绍每一个版本的更新和修改记录。微信

Android 1.X 修改记录^1
Android 1.X版本也就是第一个Android诞生的版本。从Android1.1版本开始,提供基本的通知栏消息功能,包含小图标、主标题、副标题和时间这四个元素。右上角有一个清除通知栏消息的按钮。须要说明的是,Android从一开始就提供了清除通知栏消息的功能而且保留至今,而iOS到如今都没有提供清除按钮。app

Android 2.X 修改记录^2
Android 2.X版本的通知栏消息功能上并未发生变化,右上角的“clear notifications”缩减为了“clear”。2.2版本之前沿用了1.5的通知栏样式,从2.3版本开始从新设计,改为了暗色背景。ide

Android 3.X 修改记录^3
Android 3.X版本是专为Pad而设计的系统。通知栏消息带来了一些新的功能。布局

非永久的通知栏消息的右边增长了“X”按钮,点击后该条通知能够当即清除。
增长了RemoteControlClient,即远程控制媒体应用的功能。
增长了LargeIcon,可使用大图展现通知栏消息。
Android 4.1 修改记录^4
Android 4.1版本的通知栏在3.X版本的基础上进行了大量修改。增长了很多新功能。测试

增长了Style
增长了通知栏按钮
支持通知栏展现的优先级配置
通知栏背景改成黑色透明
通知栏样式
Android 4.1通知栏最大的变化就是增长了丰富多样的Style样式。经过设置样式,能够展现更大区域的通知消息,如展现大图和多行文字,也能够展现相似邮箱收发信的样式,同时支持自定义按钮并增长点击事件。但须要注意的是,只有最顶部的那条通知栏消息能够默认展现Style样式,其余消息默认是以普通样式展现。Style能够经过Notification.Builder.setStyle(Style)进行设置。具体支持的样式有:

Notification.BigPictureStyle
大图样式,即除了普通的通知栏消息内容外,能够在通知栏消息下方展现一张大图,最大高度支持256dp。

Notification.BigTextStyle
多行文字样式,能够支持多行文字的展现。经测试,在不一样手机上可以支持的行数不同,测试过的机子,最大支持12行。

Notification.InboxStyle
收件箱样式。支持展现具备一串消息内容的会话样式,适用于短信、邮件、IM等。

通知栏按钮
通知栏消息无论是普通样式仍是Style样式,都支持两个按钮同时出如今一条通知栏消息的底部,经过这两个按钮,能够自定义一系列动做,包括回复信息和邮件,点赞等。经过Notification.Builder.addAction(Action)添加按钮。

通知栏优先级
Android 4.1通知栏增长了优先级的配置,优先级高的消息能够展现在最上方。谷歌设计优先级的初衷是根据不一样的优先级来防止用户成天被各类莫名其妙的通知栏消息骚扰,重要的通知则应该适当提升优先级,使得用户能够快速地看到并回应,不重要的通知则下降优先级,防止用户被打扰。优先级一共有5个级别,分别是:

// 默认优先级
public static final int PRIORITY_DEFAULT = 0;
// 低优先级
public static final int PRIORITY_LOW = -1;
// 最低优先级
public static final int PRIORITY_MIN = -2;
// 高优先级
public static final int PRIORITY_HIGH = 1;
// 最高优先级
public static final int PRIORITY_MAX = 2;
Android 4.3 修改记录^6
Android 4.3通知栏没有发生大的变化。主要增长了两个小功能。

增长了Notification AccessApi,容许可穿戴设备远程控制通知栏消息。
增长了NotificationListenerService,容许接收到系统通知栏列表的变化
Android 5.X 修改记录^7
Android 5.X系统相较于之前的版本,能够说是一个真正能够和iOS抗衡的系统。材料设计给Android系统注入了新的活力,相应的通知栏消息也相较于上一个版本进行了改版。所发生的变化有:

通知栏修改成白色背景,暗色字体,以适应材料设计风格。
系统会忽略全部non-alpha通道的图标,包括按钮图标和主图标。
能够经过setColor()方法在图标后设置一个背景色。
通知消息的声音将经过STREAM_RING或者STREAM_NOTIFICATION控制,之前是经过STREAM_MUSIC控制。
锁屏状态下,能够控制通知栏消息的隐私程度。
移除了RemoteControlClient,更改成NotificationCompat.MediaStyle实现。
增长了Heads-up通知,即经过状态栏浮动窗口展现通知消息。
Android 6.X 修改记录^8
移除了Notification.setLatestEventInfo()方法,经过持有Notification.Builder,而后使用build()方法能够更新同一个通知栏实例。
容许用户控制应用通知的优先级。
加入了免打扰模式(Do Not Disturb)。
增长了getActiveNotifications()方法获取当前展现的通知消息。
Android 7.X 修改记录
通知栏样式全面改版,小图标在左上角,大图标在右边,小图标、App应用名、副标题、数量和时间在第一行,第二行是主标题,第三行是内容。
增长了Notification.DecoratedCustomViewStyle()和Notification.DecoratedMediaCustomViewStyle(),帮助更好的装饰带有RemoteViews的通知栏消息。
须要动态设置Builder.setShowWhen(true)才会显示时间。
支持Action的直接回复,经过RemoteInput实现,且回复的消息内容支持当即添加到通知栏。
支持通知消息组,类似的消息在达到必定数量后会按照消息组来显示。
增长了NotificationManager.areNotificationsEnabled告知应用是否开启了通知权限。
Android通知栏踩坑与填坑指南
魅族5.X手机,大图显示问题
问题详情
Flyme系统对原生Android源码作了修改,采用BigPictureStyle方式显示大图通知栏的时候,消息与大图重合了,以下图。

Drawing

解决方案
首先说一下为何会有解决方案。展现大图这个功能开发完成后,拿去给产品演示。碰巧产品的机型就是一魅族手机T_T,结果固然是不能接受的,而后又一个巧合的事情出现了,那就是产品的手机里,京东App推了一条带大图的广告,他们竟然可以解决这个问题!因而,我开始研究解决方案。

首先,经过BigPictureStyle来实现大图功能确定是走不通的,由于事实就摆着行不通的嘛。京东的App确定是经过RemoteViews来实现的。因而,开始走弯路,尝试经过RemoteViews来展现大图。可是谷歌规定,自定义布局展现的通知栏消息最大高度是64dp。那么,京东的App是怎么实现的?在尝试了各类方法之后,最后又是经过投机取巧的方式解决了问题:

private void showBigPictureNotificationWithMZ(Context context) {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification.Builder builder = new Notification.Builder(context);
Notification notification = generateNotification(builder);
notification.bigContentView = mRemoteViews;
notificationManager.notify(notifyId, notification);
}
须要先生成Notification的实例,而后手动给notification.bigContentView赋值,再notify,就能够了

顶部状态栏(StatusBar)小图标显示异常
问题详情
当通知来的时候,若是不在通知栏浏览,会在顶部状态栏出现一个向上翻滚动画的通知消息,这条通知消息左边是一个小图标。部分系统这个小图标显示异常,是一个纯灰色的正方形,以下图。

Drawing

解决方案
首先产生灰色图标的缘由就是5.0系统引入了材料设计,谷歌强制使用带有alpha通道的图标,而且RGB的alpha值必须是0(实测不为0也是能够的,但系统会忽略全部RGB值)。所以,使用JPG的图片是不行的,最好的代替方案就是一张背景透明的PNG图片。

Android 7.X机型,通知栏小图标显示成灰色
问题详情
这个问题跟第二个有点相似,在7.0系统及以上,有部分应用的小图标是灰色的,大图能够正常显示。碰巧的是,显示异常的小图标,颜色都是灰色的。

Drawing

解决方案
与小图标显示异常解决方案相似,将小图标替换为透明背景的PNG图片。

RemoteViews显示异常
问题详情
因为系统提供的通知栏消息类型有时候不能知足要求,部分通知栏消息采用自定义RemoteViews来实现。采用RemoteViews,特别是手动生成Bitmap而后直接传给一个自定义Layout,再经过setContentView方式设置通知栏消息时,会存在各类各样的坑。

Android通知栏的背景色有几种状况,白色、暗色、暗色透明和黑色。若是生成的Bitmap带背景色,这个背景色就很难选择。若是选择黑色背景,那么在白色通知栏的机型上就很难看。所以不能彻底在各个系统上面完美展现出来。若是不带背景色,那么字体颜色也面临一样的困惑。试想,若是在白色的背景上显示白色的文字,用户看到白茫茫一片,是什么感觉?

Drawing

另外一方面,大部分厂商对原生的Android系统都会有各类各样的改造,通知栏的样式也不例外。若是按照原生的样式来设计,那么在大部分国内厂商的机子上显示都和正常的普统统知栏消息不同。例如华为6.0系统的机子,原生系统的时间线在右上角,华为的在左边,这样会给用户带来错觉。

Drawing

解决方案
详见RemoteViews适配一节。

大尺寸小图标在部分机型上显示不正确
问题详情
这个问题主要在部分机型的4.X系统上碰见,小图标大小没有按照24dp裁剪,而是采用了桌面图标同样的大小96dp。具体适配不正常的机型有HTC Desire 820、Lenovo A320T。

Drawing

解决方案
按照标准来,小图标大小为24dp,大图标为桌面icon图标大小96dp。具体可参考这里^14

部分机型不支持Style
具体机型见下图以及后面统计的表格。顺便提下,小米是其中之一,不知道他们为何不支持额外的这些Style。

Drawing

(点击查看大图)

通知栏更新频率
问题详情
每一个应用基本都有自更新的逻辑,App开机的时候提示用户升级,点击升级按钮后在Notification出现一个下载带进度条的通知。应用通常是在开启一个工做线程在后台下载,而后在下载的过程当中经过回调更新通知栏中的进度条。咱们知道,下载进度的快慢是不可控的,若是每次下载中的回调都去更新通知栏,那么可能几百毫秒、几十毫秒、甚至几毫秒就更新一次通知栏,应用可能就会ANR,甚至崩溃。

解决方案
控制通知栏更新频率,通常控制在0.5s或者1s就能够了。在某一个更新时间间隔内下载的进度回调直接丢弃,须要注意的是下载完成的回调,须要实时回调通知栏消息显示下载完成。

恶心的后台通知和“守护”通知
问题详情
这个坑我不肯多介绍,只说结果。但凡存在后台通知或者“守护”通知的应用,在7.0系统之后都会原形毕露。尚未适配7.0的应用,可长点心儿吧~

Drawing Drawing

解决方案
请弃坑。

小米推送SDK接入问题
问题详情
为了提高推送到达,考拉接入了小米推送的SDK。小米推送分为通知栏消息和透传消息,通知栏消息属于系统级推送,在MIUI的机子上能够在进程被杀死的状况下也能收到应用推送。然而有个问题,小米认为应用在前台时,不会回调任何方法;小米认为应用在后台的时候,收到通知栏消息的同时,会回调onNotificationMessageArrived方法。这时候就要当心翼翼地处理这条消息了。由于若是你的应用先后台判断逻辑和小米的不同,那么就有可能小米帮你发了一条通知栏消息,你本身又发了一遍,形成通知栏消息的重复发送(这个坑考拉踩过T_T)。另外一方面,在7.0系统的机子上,主标题和小图标的颜色是能够改变的,目前小米推送SDK没有开放这个接口供调用方定制。

解决方案
目前只能解决第一个问题——先后台判断的问题。应用是否在后台能够根据如下代码进行判断。在Android 5.0以上,能够经过ActivityManager.RunningAppProcessInfo判断,Android 5.0及如下版本经过ActivityManager.RunningTaskInfo判断。经测试,这个方案在Android 4.4以上结果是能够彻底匹配的。

public static boolean isAppInBackgroundInternal(Context context) {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
List<ActivityManager.RunningAppProcessInfo> runningProcesses = manager.getRunningAppProcesses();
if (!ListUtils.isEmpty(runningProcesses)) {
for (ActivityManager.RunningAppProcessInfo runningProcess : runningProcesses) {
if (runningProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
return false;
}
}
}
} else {
List<ActivityManager.RunningTaskInfo> task = manager.getRunningTasks(1);
if (!ListUtils.isEmpty(task)) {
ComponentName info = task.get(0).topActivity;
if (null != info) {
return !isKaolaProcess(info.getPackageName());
}
}
}
return true;
}
Android通知栏适配
RemoteViews适配
因为系统自带的通知栏消息样式不能彻底知足产品们脑洞大开的需求,有时候咱们须要自定义布局样式展现通知栏消息。Android系统能够将自定义布局经过setContent(7.X系统推荐使用setCustomContentView)设置到Notification.Builder中,来实现样式的更变。setContent方法须要传入一个RemoteViews对象,它是一个普通的数据类型,不是View,做用是供其余进程展现视图。RemoteViews只支持4种基本的布局^9:

FrameLayout
LinearLayout
RelativeLayout
GridLayout
这些布局下面只支持几种视图控件:

AnalogClock
Button
Chronometer
ImageButton
ImageView
ProgressBar
TextView
ViewFlipper
ListView
GridView
StackView
AdapterViewFlipper
只能经过上述组合生成一个RemoteViews。

自定义布局与视图
除了上面提到的布局与控件,有没有办法自定义布局与视图呢?咱们知道,任何一个View,均可以生成一个Bitmap对象,支持的视图控件里有ImageView,能够经过ImageView.setBitmapResource()将自定义视图设置到一个ImageView中,而后再随便放到一个布局上,就能够实现通知栏消息的任意布局。理想是美好的,但现实是残酷的。使用这种方式自定义的布局,会存在与原生的通知栏消息样式不一致的可能,包括小图标/大图标的大小,字体的大小与颜色,时间的显示方式(不一样版本的时间显示位置和样式都不同)。下面解决一个最关键,也最致命的问题——字体颜色。若是字体颜色和背景颜色同样,那这条通知栏消息就无法看了,如RemoteViews显示异常一节介绍的同样。

解决字体颜色和背景颜色同样的问题有三种解决方案,分别是:

背景色固定不透明,字体颜色与背景色造成反差。(360和京东的作法)
背景色透明,字体颜色采用系统原生的notification_style。
背景色透明,经过特殊方式拿到通知栏字体颜色和字体大小。
Drawing

其中,第一种方案简单,可以兼容全部厂商机型。例如京东固定背景色为黑色,字体为红色。这种方式的惟一缺陷是样式上不能与普统统知栏消息重合,在白色背景的通知栏上极为显眼。第二种方式,经过阅读源码可知,系统的通知栏标题和内容采用的颜色分别是@android:color/primary_text_dark和@android:color/secondary_text_dark,但踩过坑以后发现并不是全部的机型默认都是这两个颜色,有可能获取不到值。所以这种方案只能做为参考,不能用于实际环境中。最后详细介绍一下第三种方式。

Android默认字体颜色获取
这种方案有一点投机取巧,是网上寻找代替方案时在简书上找到的,做者是hackware。思路就是经过Notification.Builder生成一条空的Notification,但不调用notify()方法,而后经过这条Notification想办法获取里面的布局元素,经过遍历,就能拿到对应的字体和颜色了。具体看代码:

private static final String NOTIFICATION_TITLE = "notification_title";
public static final int INVALID_COLOR = -1; // 无效颜色
private static int notificationTitleColor = INVALID_COLOR; // 获取到的颜色缓存
/**

  • 获取系统通知栏主标题颜色,根据Activity继承自AppCompatActivity或FragmentActivity采起不一样策略。
  • @param context 上下文环境
  • @return 系统主标题颜色
    */
    public static int getNotificationColor(Context context) {
    try {
    if (notificationTitleColor == INVALID_COLOR) {
    if (context instanceof AppCompatActivity) {
    notificationTitleColor = getNotificationColorCompat(context);
    } else {
    notificationTitleColor = getNotificationColorInternal(context);
    }
    }
    } catch (Exception ignored) {
    }
    return notificationTitleColor;
    }
    /**
  • 经过一个空的Notification拿到Notification.contentView,经过{@link RemoteViews#apply(Context, ViewGroup)}方法返回通知栏消息根布局实例。
  • @param context 上下文
  • @return 系统主标题颜色
    */
    private static int getNotificationColorInternal(Context context) {
    Notification.Builder builder = new Notification.Builder(context);
    builder.setContentTitle(NOTIFICATION_TITLE);
    Notification notification = builder.build();
    try {
    ViewGroup root = (ViewGroup) notification.contentView.apply(context, new FrameLayout(context));
    TextView titleView = (TextView) root.findViewById(android.R.id.title);
    if (null == titleView) {
    iteratorView(root, new Filter() {
    @Override
    public void filter(View view) {
    if (view instanceof TextView) {
    TextView textView = (TextView) view;
    if (NOTIFICATION_TITLE.equals(textView.getText().toString())) {
    notificationTitleColor = textView.getCurrentTextColor();
    }
    }
    }
    });
    return notificationTitleColor;
    } else {
    return titleView.getCurrentTextColor();
    }
    } catch (Exception e) {
    DebugLog.e(e.getMessage());
    return getNotificationColorCompat(context);
    }
    }
    /**
  • 使用getNotificationColorInternal()方法,Activity不能继承自AppCompatActivity(实测5.0如下机型能够,5.0及以上机型不行),
  • 大体的缘由是默认通知布局文件中的ImageView(largeIcon和smallIcon)被替换成了AppCompatImageView,
  • 而在5.0及以上系统中,AppCompatImageView的setBackgroundResource(int)未被标记为RemotableViewMethod,致使apply时抛异常。
  • @param context 上下文
  • @return 系统主标题颜色
    */
    private static int getNotificationColorCompat(Context context) {
    try {
    Notification.Builder builder = new Notification.Builder(context);
    Notification notification = builder.build();
    int layoutId = notification.contentView.getLayoutId();
    ViewGroup root = (ViewGroup) LayoutInflater.from(context).inflate(layoutId, null);
    TextView titleView = (TextView) root.findViewById(android.R.id.title);
    if (null == titleView) {
    return getTitleColorIteratorCompat(root);
    } else {
    return titleView.getCurrentTextColor();
    }
    } catch (Exception e) {
    }
    return INVALID_COLOR;
    }
    private static void iteratorView(View view, Filter filter) {
    if (view == null || filter == null) {
    return;
    }
    filter.filter(view);
    if (view instanceof ViewGroup) {
    ViewGroup viewGroup = (ViewGroup) view;
    for (int i = 0; i < viewGroup.getChildCount(); i++) {
    View child = viewGroup.getChildAt(i);
    iteratorView(child, filter);
    }
    }
    }
    private static int getTitleColorIteratorCompat(View view) {
    if (view == null) {
    return INVALID_COLOR;
    }
    List textViews = getAllTextViews(view);
    int maxTextSizeIndex = findMaxTextSizeIndex(textViews);
    if (maxTextSizeIndex != Integer.MIN_VALUE) {
    return textViews.get(maxTextSizeIndex).getCurrentTextColor();
    }
    return INVALID_COLOR;
    }
    private static int findMaxTextSizeIndex(List textViews) {
    float max = Integer.MIN_VALUE;
    int maxIndex = Integer.MIN_VALUE;
    int index = 0;
    for (TextView textView : textViews) {
    if (max < textView.getTextSize()) {
    // 找到字号最大的字体,默认把它设置为主标题字号大小
    max = textView.getTextSize();
    maxIndex = index;
    }
    index++;
    }
    return maxIndex;
    }
    /**
  • 实现遍历View树中的TextView,返回包含TextView的集合。
  • @param root 根节点
  • @return 包含TextView的集合
    */
    private static List getAllTextViews(View root) {
    final List textViews = new ArrayList<>();
    iteratorView(root, new Filter() {
    @Override
    public void filter(View view) {
    if (view instanceof TextView) {
    textViews.add((TextView) view);
    }
    }
    });
    return textViews;
    }

private interface Filter {
void filter(View view);
}
使用这种方法,咱们统计并测试了大厂商的部分机型,获得以下表格:

厂商 机型 系统通知标题颜色 背景主题 是否支持大图/多行文字 系统版本 标题大小 可否获取App推送是否打开 备注
华为 荣耀V8 未知 暗色 支持 6.0 14dp/19dp 能 通知栏显示须要受权
华为 P8 -637534209 暗色透明 支持 5.0.1 能
华为 荣耀4A -637534209 黑色,透明度不高 支持 5.0.1 能
华为 荣耀7 -637534209 黑色,透明度不高 支持 5.0.2 能
华为 MATE8 -637534209 黑色 支持/支持12 6 14dp/16dp+ 能
华为 G7plus -637534209 黑色 支持 5.1 14dp 能
华为 荣耀4X -637534209 黑色,透明度不高 支持 5.0.2 14dp/16dp+ 能
小米 3 -452984832 灰色,不透明 不支持/不支持 4.4.4 能 工做线程获取系统颜色时,会产生java.lang.reflect.InvocationTargetException,带emoji表情时,系统显示颜色和普通推送同样
小米 5s -452984832 白色,不透明 不支持 6.0.1 13.33dp/18dp 能
红米 NOTE -1 灰色,不透明 不支持 4.4.4 能 带emoji表情时,系统显示颜色和普通推送同样
红米 NOTE3 -1 灰色,透明 不支持 5.1.1 能 带emoji表情时,系统显示颜色和普通推送同样,但样式与普通推送稍有不一样
三星 S4 -16777216 白色,不透明 支持 5.0.1 17dp/17dp 能 大图须要下拉才能显示
三星 S5 -16777216 白色,不透明 支持 5 能 大图须要下拉才能显示
三星 S6 -14342875 白色,不透明 支持 6.0.1 17dp/17dp 能 大图不须要能够显示,系统版本6.0.1
三星 Note2 -1644826 黑色 支持 能
三星 S6+ -14342875 白色,不透明 支持/支持 5.1.1 16dp 能
三星 SM-N9100 -16777216 白色,不透明 支持 17dp/18dp+ 能 5.0.1
魅族 MX5 -1 灰色,不透明 支持/支持6 5.1 18dp/21dp 能 小通知和大图重叠;带emoji表情时,系统显示内容颜色为Android自带颜色,与魅族的系统推送颜色不太同样
魅族 MX3 4.3 不能 系统是联通定制的,版本也比较低,估计还不支持该API。
魅蓝 NOTE2 -1 灰色,透明度不高 支持 5.1 18dp/21dp 能 小通知和大图重叠;带emoji表情时,统显示内容颜色为Android自带颜色,与魅族的系统推送颜色不太同样
HTC Desire820 -1 黑色,不透明 支持 4.4.4 未测试 有不可清除的系统消息存在时,大图默认不显示;普通推送小icon图标显示不正确;自定义emoji表情跟MX5相似;工做线程获取系统颜色时,会产生java.lang.reflect.InvocationTargetException
HTC M9w -570425344 白色 支持 未测试
OPPO R7plus -1 黑色,透明度不高 不支持 5 16dp/16dp 能 带emoji表情时,系统显示颜色和普通推送同样
OPPO R3 黑色,透明度不高 不支持 4.3 18dp/18dp 未测试
一加 ONE -570425344 白色 支持 5.1.1 16dp/16dp 能
一加 One plus 3 白色 支持/支持13 7.0 能
LG Nexus5 -570425344 白色 支持/支持12 6.0.1 16dp/16dp 能
乐视 MAX -1 暗色,透明 支持 6.0.1 16dp/16dp 能
ViVO X5L -1 暗色 支持 4.4.2 未测试
ViVO X5M -1 暗色 支持 5.0.2 能
锤子 T1 黑色 不支持 4.4.2 未测试
金立 S5.1 -1 黑色,不透明 支持 4.3 未测试 工做线程获取系统颜色时,会产生java.lang.reflect.InvocationTargetException
联想 K910 -1 暗色 支持 4.2.2 不能
联想 A320T -1 黑色,不透明 支持 4.4.4 未测试
能够看到,通知栏存在各式各样的背景色,字体大小和颜色也不尽相同。经过上述方法,有一部分机型是拿不到系统通知栏颜色的,但经过观察能够发现,全部拿不到字体颜色的机型都是暗色或黑色背景(实测7.0此经验失效),所以可使用白色字体。

考拉RemoteViews适配方案
通过上述调研与测试,咱们的适配方案以下:

获取系统通知标题颜色,若是可以获取到,那么标题、内容和时间的颜色都设置为标题颜色。
获取不到的状况下,遍历系统通知里的全部文字,取字号最大的那条文字的颜色做为标题、内容和时间的颜色。
以上两个步骤的实如今getNotificationColor()方法里。若是还获取不到,那么标题和内容采用Android原生系统提供的,其中标题是@android:color/primary_text_dark,内容是@android:color/secondary_text_dark。
有一点须要说明的是,以上适配只适合在Android 7.0如下系统。Android 7.0+修改了Notification,采用@android:color/primary_text_dark和@android:color/secondary_text_dark已经获取不到颜色值了,考虑到7.0所采用的通知栏主色调是白色,所以目前暂时的解决方案是遇到7.0的系统采用黑色字体。面对众多厂商的源码修改,目前测试有ZUK的7.0系统为暗色背景,暂时的解决方案是根据机型适配。
Emoji表情适配^12
对于Android emoji表情的适配,我想只有体验过的人才知道这里面有多少坑。我试图经过谷歌了解Android在每个版本对应的emoji表情的支持状况,最终发现没有一篇文章或一个网页可以彻底列出emoji表情在Android上的修改历史。因而我只能本身动手,构建一张Android emoji表情支持版本对比的表格。

Android系统版本 emoji版本 Unicode版本 Unicode emoji发布日期 Android支持版本日期 emoji表情数 新增表情连接/更新日志 备注
7.1 4 9.0 2016.07.21 2016.10.20 2374 http://emojipedia.org/google/android-7.1/new/ http://blog.emojipedia.org/android-7-1-emoji-changelog/ 成为首个支持Unicode 9标准的系统!
7.0 3 9.0 2016.07.21 2016.08.22 1791 http://emojipedia.org/google/android-7.0/new/ http://blog.emojipedia.org/android-7-0-emoji-changelog/
6.0.1 1 8.0 2015.07.17 2015.12.07 1294 http://emojipedia.org/google/android-6.0.1/new/ http://blog.emojipedia.org/android-6-0-1-emoji-changelog/
6.0 1 7.0 2014.07.16
5.0 1 6.1 2012.02 2014.11.03 1090 http://emojipedia.org/google/android-5.0/new/ http://blog.emojipedia.org/android-50-emoji-changelog/
4.4 1 6.0 2010.1 2014.11.01 850 http://emojipedia.org/google/android-4.4/new/ 首个支持彩色Emoji表情的Android系统
4.3 1 6.0 2010.1 2013.07.24 717 http://emojipedia.org/google/android-4.3/new/ 首个支持Emoji表情的Android系统,但颜色是黑白的
首先须要说明的是为何会有Emoji版本和Unicode版本?Emoji实际上能够说是Unicode下的一个子集,Unicode的版本更新,除了Emoji表情发生变化之外,还有许多其余的字符集定义发生变化,Emoji版本是跟随着Unicode版本的更新而逐渐迭代更新的。能够看到,基本上一个Unicode版本对应着一个Emoji版本。目前最新的Unicode版本规划是Unicode 11.0^10,最新的Emoji版本规划是Emoji 6.0^11,实际待发布版本是Unicode 10.0和Emoji 5.0,将在2017年中旬发布。

固然这只是官方Android系统所支持的emoji版本,面对众多的厂商对源码大刀阔斧的修改,结果又是怎样呢?咱们拿了33个表情来进行测试,其中大部分表情是Unicode 6.0标准,后面几个表情是Unicode8.0标准。最终获得了以下结果。

Drawing
(点击查看大图)

实际测试结果与上面的表格基本匹配。特别表扬一下魅族,在5.X系统上就已经支持了Unicode 8.0标准!乐视的系统在6.0.1系统上的表现指明支持的是Unicode 7.0标准,实际上Android原生已经支持Unicode 8.0标准了。

所以,emoji表情的适配其实相对较简单,就是根据不一样的系统版本实现不一样的支持。固然,若是须要简化那么让只须要让运营配置Unicode 6版本的emoji表情就可以适配4.4+版本的系统了!至于4.4如下版本,能够把经常使用的Emoji表情放到资源文件中,遇到文本中包含Emoji字符时,手动替换成资源文件中的Emoji图片,再经过上述RemoteViews方式来显示。

Android Nougat+适配
从上面的介绍中,你们能够发现,Android 7.0系统之后通知栏消息改版了。援引官方在Notifications public deck中介绍的一张图,

Drawing

除了基本的样式发生变化,在7.0中也作了部分接口上的修改。其中,咱们须要“拥抱变化”的内容有:

使用non-alpha图标
在5.0修改记录中,有一条系统会忽略全部non-alpha通道的图标,包括按钮图标和主图标。这句话是什么意思呢?实际上,Android从5.0系统开始,对于通知栏图标的设计进行了修改。如今Google要求,全部应用程序的通知栏图标,应该只使用alpha图层来进行绘制,而不该该包括RGB图层。通俗点来说,就是让咱们的通知栏图标不要带颜色就能够了^13。这也是上面的截图中为何这么多应用都显示不出icon图标,而是显示成灰色的正方形。缘由就是他们用了带背景的图片。

Notification.Builder.setColor()方法
5.0加入的Notification.Builder.setColor()方法,本来是渲染小图标的背景色的,7.0之后,改为了渲染的是通知栏消息第一行的颜色。

Drawing
Drawing

7.0系统默认不显示时间
7.0系统之后须要显式调用Notification.Builder.setShownWhen(true)才会显示时间,而且格式调整为具体发布时间相差N小时/N天的形式(见上图)。

RemoteViews样式调整
若是要适配7.0之后的样式,可使用如下两个包裹的Style,将RemoteViews封装在内容区域。

Notification.DecoratedCustomViewStyle()
Notification.DecoratedMediaCustomViewStyle()
Drawing

图中是未包裹与包裹时候的展现效果。能够说适配RemoteViews是很是蛋疼的一件事,应用能够根据须要来选择是否使用DecoratedCustomViewStyle。

若是不是必要,建议不要使用RemoteViews。考拉以前是为了兼容在不一样手机厂商上展现的emoji表情不一致,以及兼容低版本系统,而在包含emoji表情的消息推送中使用了RemoteViews。通过测试,前者的兼容彻底没有必要,在下一版的平常版本发布中会移除RemoteViews相关内容,改用原生通知栏消息实现。

Android O通知栏新特性一览
就在笔者即将发布这篇文章的时候,Android O系统发布了预览版!由上面的讨论可知,几乎每一个Android版本都修改了Notification,相信Android O也不例外。笔者火烧眉毛地查看了新系统特性以及修改历史,果不其然,Android O的通知栏消息又双叒叕改版了!关于新系统的通知栏消息改变以下:^15

Notification channels
Android O 还引入了通知渠道,这是全新的由应用定义的通知内容类别。借助渠道,开发者可让用户对不一样种类的通知进行精细控制,用户能够单独拦截或更改每一个渠道的行为,而不是统一管理应用的全部通知。^16

简单说就是增长了应用级别的通知栏消息分组功能。举个例子,用户能够分别控制微信群组和微信我的在通知栏的显示级别,群组消息混杂,能够调整较低的显示级别;而我的消息相对重要,能够调整为较高的级别。

Snoozing
有点相似闹钟的打盹儿功能。用户可让一条打盹儿了的通知栏消息再次出如今通知栏上。开发者能够移除或更新一条打盹儿消息,但更新这条消息不会让已经处于打盹儿状态的通知栏消息再次展现到通知栏上。

Notification timeouts
建立一条通知栏消息时,支持设置消息有效期,超过有效期后通知栏消息会被系统取消。经过Notification.Builder.setTimeout()方法设置。

Notification dismissal
新系统提供了API,区分一条通知是被用户移除或者被应用(即开发者)移除。经过NotificationListenerService.onNotificationRemoved()方法能够监听获得。

Background colors
新系统提供了API设置通知栏消息的背景颜色。值得注意的是,应当谨慎使用这个API,只有当消息很是紧急,必须通知到用户的时候,才须要设置背景色。例如,能够为一个正在导航的应用,或者来电设置一个背景色。能够经过Notification.Builder.setColor()或者Notification.Builder.setColorized()设置。

因为目前没有真机/模拟器,笔者在这里有一个疑惑。以前Notification.Builder.setColor()这个方法在Android N上设置的是通知栏消息第一行的颜色,包括图标、应用名称、副标题等。而在Android O上变成了修改整个消息的背景色?在真机/模拟器available的时候将测试一下。

Messaging style
设置了Messaging style风格的消息在新系统上可以展现更多的内容。消息导向(messaging-related)的通知栏消息应该使用MessageStyle风格代替原生消息。开发者也可使用新的addHistoricMessage()方法将消息添加到通知栏中,以便提供对话的上下文信息。

Notification channels
Android O系统引入了通知渠道,相似于分组的概念。通知渠道须要开发者手动建立,一个应用能够建立多个通知渠道,用户能够分别管理应用的每一个通知渠道,管理页面由系统提供统一的UI。全部分配到同一个渠道的消息,表现都同样。当用户修改了某个渠道的如下任一种行为,都会同步到该渠道的任何一条消息:

Importance
Sound
Lights
Vibration
Show on lockscreen
Override do not disturb
一旦通知渠道被建立并提交到通知栏管理器(NotificationManager),那么开发者就没有权限修改通知渠道的任何配置了,全部的配置只能由用户修改。用户能够到“设置”页面,或者长按通知栏消息改变通知渠道的配置。

Notification Priority and Importance
Android O系统弃用了旧的通知栏优先级,并提出了通知栏消息重要性这个概念。通知栏上的消息展现顺序再也不由优先级控制,也没法使用重要性来控制。重要性能够控制消息展现在什么地方,例如默认级别IMPORTANCE_DEFAULT(3)能够展现在任意地方,如通知栏、状态栏、锁屏,能够发出通知声音,但不直接展现给用户,即不会弹出heads-up通知。重要性一共有6个级别:

IMPORTANCE_NONE(0)
IMPORTANCE_MIN(1)
IMPORTANCE_LOW(2)
IMPORTANCE_DEFAULT(3)
IMPORTANCE_HIGH(4)
IMPORTANCE_MAX(5)
开发者只能设置IMPORTANCE_NONE(0)至IMPORTANCE_HIGH(4)级别,最高级别不能经过代码控制。重要性控制也是针对通知渠道级别的,具体有没有全局性控制得看具体的模拟器设置。

总结
本文介绍了Android通知栏消息随着系统的更新所发生的变化以及在各个版本的Android系统通知栏消息适配过程当中所产生的一些问题,并提供解决思路。随着Android版本的逐渐迭代,能够预见Android通知栏消息将会支持愈来愈多可配的样式,也逐渐地把权限交给用户控制,包括消息的展现以及隐私的设置。文中提到的后台通知以及“守护”通知,是对付部分应用为了常驻内存保持进程不被杀而采起的措施,这对于国内的Android生态来讲无疑是利好的。

参考连接 https://arstechnica.com/gadgets/2016/10/building-android-a-40000-word-history-of-googles-mobile-os/8/#h1 https://arstechnica.com/gadgets/2016/10/building-android-a-40000-word-history-of-googles-mobile-os/10/#2.0eclair https://arstechnica.com/gadgets/2016/10/building-android-a-40000-word-history-of-googles-mobile-os/16/#honeycomb https://developer.android.com/about/versions/android-4.1.html http://www.androidpolice.com/2012/07/04/getting-to-know-android-4-1-part-2-the-glorious-new-notifications-size-matters/ https://developer.android.com/about/versions/android-4.3.html https://developer.android.com/about/versions/android-5.0-changes.html#BehaviorNotifications https://developer.android.com/about/versions/marshmallow/android-6.0-changes.html https://developer.android.com/guide/topics/appwidgets/index.html#CreatingLayout http://emojipedia.org/unicode-11.0/ http://emojipedia.org/emoji-6.0/ http://emojipedia.org http://blog.csdn.net/guolin_blog/article/details/50945228 http://iconhandbook.co.uk/reference/chart/android/ https://developer.android.com/preview/api-overview.html http://developers.googleblog.cn/2017/03/android-o-developer-preview.html

相关文章
相关标签/搜索