今天是端午节,祝大家节日快乐
这篇文章着实酝酿了许久,一直懒得写。网上关于通知栏样式适配的文章很多,但还不够完美,这也是我写这篇文章的动力所在。
温故而知新
Android通知有两种,默认通知与自定义通知。默认通知简单调用系统接口就能实现,如下:

发送默认通知

默认通知效果
自定义通知就稍微麻烦一些,需要定义一个layout文件,使用RemoteViews加载它并设置一些点击事件,再设置到builder,如下:

自定义通知代码

自定义通知效果
这个通知很简单,就是两行文本加上一个按钮,按钮具有单独的点击事件,点击后跳转到AnotherActivity。
注意:smallIcon对于自定义通知和默认通知都是必须的,否则通知显示不出来。道理很简单,smallIcon需要在状态栏上显示,不设置怎么行。在5.0及以上,smallIcon必须符合Material Design风格,即白色内容,透明背景。不然系统会使用默认的图片替换。具体可参考Android通知栏微技巧,那些你所没关注过的小细节 标签: android通知通知栏微技巧。后面我会有一篇更详细的文章来介绍这个。contentIntent对于2.3及以下的系统是必须的,否则发送通知时会抛异常。道理也很简单,Android 2.3及以下系统不支持给自定义通知上的元素绑定单独的点击事件,因此必须设置整个通知的点击事件。
为什么要进行样式适配?
默认通知不存在样式适配的问题,因为默认通知的布局、颜色、背景什么的都是系统的,系统总会正确的显示默认通知。但自定义通知就不一样了,自定义通知的布局完全由我们自己掌控,我们可以为元素设置任何背景、颜色。那么,问题来了。Android通知栏的背景各种各样,不同的ROM有不同的背景,白色、黑色、透明等。不同的Android版本通知栏背景也不一样,一旦我们为自定义通知上的元素设置了特定背景或颜色,就肯定会带来兼容性问题(主要是文本啦)。这样的应用一大把,贴个图大家就明白了:

未适配的自定义通知
怎么适配?
适配的方式大概有两种,一种简单粗暴:为自定义通知设置固定的背景(上图中的360卫士就这么干的),比如黑色。那么内容自然就是白色或近似白色。这样,在所有的手机上都能正常显示,不会出现在黑色背景通知栏上显示良好,到了白色背景通知栏上就几乎啥也看不见。使用这种方案的应用太多了。我个人很不推崇这种方式,这样会使得自定义通知在将近一半的手机上显示得很突兀,和系统的通知栏不够沉浸,很丑。另一种方案就稍微合理一些:通过读取系统的通知栏样式文件,获取到title和content的颜色,进而将颜色设置到自定义通知上。读取通知栏样式文件本身有兼容性问题,不同Android版本的样式文件有变,具体可参考这篇博客 通知栏设置系统字体颜色 ,这种方式也不是在所有手机上生效,实际测试发现,还是有小部分机型没法读取或是读取到的是错误的。拿到title和content的颜色后,还可以通过算法(后面细说)判断这个颜色是近似白色还是近似黑色,进而能判断出通知栏的背景是近似黑色还是近似白色,这样就能根据不同的通知栏背景加载不同的自定义通知布局。进而做到良好的适配。
更好的适配
现在切入主题,谈谈如何来更好的适配自定义通知。有过锁屏开发经验的人应该知道,如果你的应用有读取系统通知栏的权限,那么每当应用程序发出一个通知,你的应用都会收到对应的notification对象,这个时候,我们一般会执行以下操作:

获取并展示app通知
调用addView之后,应用程序的通知就会显示在我们的应用里,我们并没有做任何其他操作,可见,notificationItemLayout本身就是带有样式的,即便是默认通知。那么方案来了!我们先构造一个默认通知:

获取通知栏title的颜色
通知并不发送出去,只是用来获取通知栏title的颜色,如果你还想获取content的颜色,抱歉,不能通过查找android.R.id.text来获取,这个字段是访问不到的。可通过反射获取,也可预先设置一个content,然后遍历viewGoup根据content内容找到对应的TextView再获取颜色。
拿到颜色后,可根据算法判断这个颜色是近似白色还是近似黑色,我们使用黑色作为基准色,使用方差来计算这个颜色是否近似黑色:

比较两个颜色是否近似
baseColor传入Color.parseColor("#000000"),color传入刚刚获取到的title的颜色,根据我实测,阈值为180.0较为合理。上述方法返回true,即表示title的颜色近似黑色,也就是说通知栏背景近似白色。
实际测试
拿到了通知栏背景的颜色后,我们就可以加载不同样式的布局,达到适配的目的。代码如下:

适配代码

Android 4.4黑色背景的通知栏

坚果手机白色版白色通知栏
注意:使用此方法时,Activity不能继承自AppCompatActivity(实测5.0以下机型可以,5.0及以上机型不行),因为在5.0以下机型上ImageView已经被替换成AppCompatImageView,而AppCompatImageView的setBackgroundResource(int)未被标记为RemotableViewMethod,导致apply时抛异常。
解决办法是将getNotificationColor()方法替换成以下实现即可:

解决Activity不能继承AppCompatActivity的问题
好了,我的第一篇技术博客到此为止,大家假期玩得Happy!