背景

最近需要实现一个自定义Notification的功能。网上找了找代码,解决方案就是通过RemoteViews来实现。但是在实现过程中遇到不少问题,网上也没有很好的文章描述这些问题,所以在这里做个总结,希望大家能少走点弯路。

实现

RemoteViews 自定义View

这是最基础的知识点,虽然做过自定义通知的应该都清楚,但我觉得还是有必要带一下。它主要被用于AppWidget和Notification,它描述一个在其它进程中显示的View。以下是例子代码。从中我们可以看到RemoteViews提供了一些方法来改变它的子View的值,如设置TextView的文字等。

RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.view_notification_type_0);
remoteViews.setTextViewText(R.id.title_tv, title);
remoteViews.setTextViewText(R.id.content_tv, content);
remoteViews.setTextViewText(R.id.time_tv, getTime());
remoteViews.setImageViewResource(R.id.icon_iv, R.drawable.logo);
remoteViews.setInt(R.id.close_iv, "setColorFilter", getIconColor());
Intent intent = new Intent(context, MainActivity.class);
intent.putExtra(NOTICE_ID_KEY, NOTICE_ID_TYPE_0);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
int requestCode = (int) SystemClock.uptimeMillis();
PendingIntent pendingIntent = PendingIntent.getActivity(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.notice_view_type_0, pendingIntent);
int requestCode1 = (int) SystemClock.uptimeMillis();
Intent intent1 = new Intent(ACTION_CLOSE_NOTICE);
intent1.putExtra(NOTICE_ID_KEY, NOTICE_ID_TYPE_0);
PendingIntent pendingIntent1 = PendingIntent.getBroadcast(context, requestCode1, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.close_iv, pendingIntent1);

这里有几点需要注意的。

setInt

这个方法被用来调用子View中需要一个Int型参数的方法。如下面这句代码,调用了id为close_iv的setColorFilter方法,参数为getIconColor()的返回值。

remoteViews.setInt(R.id.close_iv, "setColorFilter", getIconColor());

设置不同区域的点击PendingIntent

默认的Notification只能通过setContentIntent设置整体的点击事件。不过通过RemoteViews我们可以设置不同地方不同的点击事件,当然这里的事件指的是PendingIntent。如下,设置了点击R.id.notice_view_type_0打开一个Activity,而点击R.id.close_iv会发出一个广播,可以通过这个广播的广播接收器来做一些事情,如这里是关闭当前的Notification。另外还可以打开一个Service。

PendingIntent.getActivity(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.notice_view_type_0, pendingIntent);
int requestCode1 = (int) SystemClock.uptimeMillis();
Intent intent1 = new Intent(ACTION_CLOSE_NOTICE);
intent1.putExtra(NOTICE_ID_KEY, NOTICE_ID_TYPE_0);
PendingIntent pendingIntent1 = PendingIntent.getBroadcast(context, requestCode1, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.close_iv, pendingIntent1);

设置通知的自定义View

以上我们得到了自定义的RemoteViews。通过下面这段代码就能生成自定义View的Notification,注意这里使用了setContent()方法。这是网上自定义Notification都会使用的方法。

Notification notification = new NotificationCompat.Builder(context).setContent(remoteViews).build();

但是它会有一个问题。

通过setContent()方法获得的Notification是定高的。如果View的高度比默认高度要大的话,就有一部分显示不出来。如下图

默认情况下通知高度为64dp,当然Rom不同可能会有些区别。一般文字在小于两行的情况下都是可以显示。

那么如何做到wrap_content。需要使用一些黑科技。如下:

NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
if(android.os.Build.VERSION.SDK_INT >= 16) {
notification = builder.build();
notification.bigContentView = remoteViews;
}
notification.contentView = remoteViews;

为了理解以上代码,我们需要明确一个我们很容易忽略的问题,那就是通知是可以展开和收起的。请看以下两张图片。同样是网易云音乐的通知,图一比图二要大一些。其实图一展示的是网易云音乐通知的展开状态,使用两个手指上滑就可以缩起,也就是图二。

在上面的代码中我们分别设置了bigContentView 这是展开的自定义视图,而contentView则是收起时的视图。

注意bigContentView是在sdk16时引入的,所以需要判断一下。如果小于sdk16则只能定高了。

注意bigContentView 的最大高度是256dp

注意bigContentView和contentView的设置不能调转顺序,亲测这样会让contentView不显示。

另外需要注意某些Rom可能不支持展开收起通知,在设置了BigContentView之后就只显示展开的视图,而默认情况下只展示收起视图。如魅族的FlyMe,其它Rom并没有测试,如果读者知道可以分享一下。

背景色适配

不同Rom的通知背景色是不同的,所以在UI上需要注意。 主要分为两种情况。

  • 背景色为有透明度的黑色,如MiUi、FlyMe。
  • 背景色为白色,如原生的5.0之后的Rom、华为部分Rom。

主要有两种方案。

固定背景色

也就是设置一个固定的背景色,文字和icon颜色都可以固定。如下图。这有一个缺点,我们在图中也看到了,那就是某些Rom的Notification会有一个左右的padding,如MIUI的就特别明显,如果固定背景色就会很难看。所以这种方法虽然简答,但是不建议使用。

透明背景色

另一种方法就是让背景透明。那么文字和icon的颜色怎么办呢?很简单,跟随系统的Notification中文字的样式。如下设置了TextView的style为默认通知中info的样式。其它相关Style包括TextAppearance.StatusBar.EventContent.Line2、TextAppearance.StatusBar.EventContent.Info等。

        <TextView
android:id="@+id/content_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Info"
tools:text="41个同校小伙伴参与讨论"
android:layout_marginTop="4dp"
android:singleLine="true"/>

需要注意的一点是Android5.0之后使用了不同的Style名表示通知样式。 我们需要创建一个layout-v21文件夹,并新建一个在5.0之后使用的自定义通知样式。如下同样是设置TextView的style为Info的样式,但我们使用的是style是@android:style/TextAppearance.Material.Notification.Info。

<TextView
android:id="@+id/content_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="9sp"
android:textAppearance="@android:style/TextAppearance.Material.Notification.Info"
tools:text="41个同校小伙伴参与讨论"
android:layout_marginTop="4dp"
android:singleLine="true"/>

另外如果自定义view中有Icon,那么Icon的颜色也需要适应背景,可以选择一个灰色,如#999999,原生安卓黑色和白色的文字内容颜色都为该值。

或者根据不同的背景色设置不同的颜色,通过上面提到的setInt方法。ImageView的setColorFilter方法可以设置图案颜色为某种纯色。但是目前我还没有找到很好的方法获取默认通知的背景色,如果读者找到了望告知。

remoteViews.setInt(R.id.close_iv, "setColorFilter", getIconColor());

最终效果

总结

以上即为我在自定义Notification中遇到的一些问题以及解决方案。目前还有两点有待进一步补充和完善。

  • 获取默认通知背景色,或者使图标颜色与背景色适配的方案。
  • 不支持Notification展开收起的Rom,目前知道的仅有FlyMe。

示例代码地址

https://github.com/beautifulSoup/CNotification

Android自定义Notification并没有那么简单的更多相关文章

  1. android自定义Notification通知栏实例

    项目有个需求,需要在发送Notification的时候动态给定url的图片.大概思路如下:自己定义一个Notification的布局文件,这样能够很方便设置View的属性. 首先加载网络图片,使用Bi ...

  2. Android: 自定义Tab样式,一种简单的方式。

    之前看到过论坛里已经有人发过自定义Tab样式的帖子,感觉有些复杂了,这里分享个简单的方法. 1.制作4个9patch的tab样式,可参考android默认的资源 tab_unselected.9.pn ...

  3. 简单说说Android自定义view学习推荐的方式

    这几天比较受关注,挺开心的,嘿嘿. 这里给大家总结一下学习自定义view的一些技巧.  以后写自定义view可能不会写博客了,但是可以开源的我会把源码丢到github上我的地址:https://git ...

  4. Android自定义view(一):制作一个最最最简单的自定义view

    转载:https://blog.csdn.net/wsyizmao/article/details/78491422 浅谈安卓自定义view(一):制作一个最最最简单的自定义view 对于安卓程序员来 ...

  5. Android -- 系统和自定义Notification

    Notification是一种让你的应用程序在不使用Activity的情况下警示用户,Notification是看不见的程序组件警示用户有需要注意的事件发生的最好途径. 作为UI部分,Notifica ...

  6. android的几种“通知”方式简单实现(Notification&NotificationManager)

    关于通知Notification相信大家都不陌生了,平时上QQ的时候有消息来了或者有收到了短信,手机顶部就会显示有新消息什么的,就类似这种.今天就稍微记录下几种Notification的用法.3.0以 ...

  7. Android 通知栏Notification的整合 全面学习 (一个DEMO让你完全了解它)

    在android的应用层中,涉及到很多应用框架,例如:Service框架,Activity管理机制,Broadcast机制,对话框框架,标题栏框架,状态栏框架,通知机制,ActionBar框架等等. ...

  8. Android 中Notification的运用

    Notification在手机的运用中是很常见的,比如我们收到一个短信,在我们的通知栏就会显示一个消息的图标用来提示我们,这种我们就可以用Notification来实现.他有很多的用法,比如类似消息的 ...

  9. Android开发——Notification通知的使用及NotificationCopat.Builder常用设置API

    想要看全部设置的请看这一篇 [转]NotificationCopat.Builder全部设置 常用设置: 设置属性 说明 setAutoCancel(boolean autocancel) 设置点击信 ...

随机推荐

  1. 2016022603 - redis数据类型

    Redis支持5种类型的数据类型 1.字符串:Redis字符串是字节序列.Redis字符串是二进制安全的,这意味着他们有一个已知的长度没有任何特殊字符终止,所以你可以存储任何东西,512兆为上限.[类 ...

  2. 关于Android中传递数据的一些讨论--备用

    在Android中编写过程序的开发人员都知道.在Activity.Service等组件之间传递数据(尤其是复杂类型的数据)很不方便.一般可以使用Intent来传递可序列化或简单类型的数据.看下面的代码 ...

  3. CSS3随笔系列之transform(一)—— transform-origin

    transform-origin属性平时似乎用得很少,它决定了变换时依赖的原点.基本的属性特性可以参考CSS手册. 如果在H5动画项目中,用到旋转的话,它还是不能小觑的. 假如我们做一个秋千效果 其实 ...

  4. Java中getAttribute getParameter 区别

            网上说的不少,发现都是同一篇,汗..... (1)HttpServletRequest类有setAttribute()方法,而没有setParameter()方法 (2)当两个Web组 ...

  5. iPhone开发 - 常用库

    iPhone开发 - 常用库 这里总结了iPhone开发者开发过程中可能需要的一些资源 如何用Facebook graphic api上传视频: http://developers.facebook. ...

  6. Java语言基础(一) 标识符

    Java标识符的问题: ①不可以以数字开头 int 123number = 0; //错误 ②可以使用任意的货币符号(¥和$等等)中文也可以  int $i = 0; //正确 int ¥i = 1; ...

  7. Storm学习笔记

    1.如何让一个spout并行读取多个流? 方法:任何spout.bolts组件都可以访问TopologyContext.利用这个特性可以让Spouts的实例之间划分流. 示例:获取到storm集群sp ...

  8. cout输出字符串指针

    先给出通过字符型指针输出字符串的示例代码,如下: #include <iostream>using std::cout;using std::endl; int main(){ const ...

  9. 通过jquery-ui中的sortable来实现拖拽排序

    1.引入文件 <script src="{sh::PUB}js/jquery-1.10.1.min.js"></script> <link rel=& ...

  10. Research Papers

    NSE, $\bbu$ [Papers]NSE, $u$, Lorentz space [Sohr, JEE, 2001] [Papers]NSE, $u$, Lorentz space [Bjorl ...