Android之Notification篇
2014-12-05 18:05
330 查看
Notification,作为Android系统提醒用户的另外一种方式,区别于Toast的一闪而过,Notification可以用于诸如短信息,事件提醒等一些在用户没有注意时的提示信息。
这个notification一般用在电话,短信,邮件,闹钟铃声,在手机的状态栏上就会出现一个小图标,提示用户处理这个通知,这时手从上方滑动状态栏就可以展开并处理这个快讯。已添加的Notification.Builder,使其更容易构建通知。
Notification是一种让你的应用程序在没有开启情况下或在后台运行警示用户。它是看不见的程序组件(Broadcast Receiver,Service和不活跃的Activity)警示用户有需要注意的事件发生的最好途径。
甚至能显示图片的预览图,有些时候,你甚至不需要打开系统的信息,直接在Notification中,就可以完全完成阅读。Android提供了两种扩展的布局
模板(文本和图片,如图所示)。
Max:提示一些重要并且紧急的通知,主要适用于,在开始某项任务前,必须要解决和处理的提示信息。
High:提示一些比较重要的信息,比如短信等聊天内容。
Default:系统默认的级别,在同是Default级别的其他提醒,不会覆盖掉当前的提醒。
Low:提示用户,一些紧迫性较低的提醒。
Min:最低级别,只有用户在滑下通知栏时,才会显示提示信息,否则是不会出现在状态栏中。比如一些天气信息的提醒。
当然,也可以使用一些扩展性的布局,通过堆栈的方式,获取更好的用户体验。
,这是我们在设计app时很容易出现的问题,总想与众不同,结果用户体验很差。
官方文档中,推荐我们调用一些LED提醒,这样在屏幕关闭的情况下,比如通过呼吸灯的颜色,来提醒用户。在设计app时,提示信息要友好,一些不必要的,
不重要的提醒,就不要骚扰用户啦,小心用户烦了,分分钟干掉你……
细心的你可能会发现,这种写法已经过时,随着Google的推新除陈,在v4的包中准确的说是在Android3.0(API11),引入了一种更加优雅的写法,先脑补一下v4,v7,v13:
Android Support v4: 这个包是为了照顾1.6及更高版本而设计的,这个包是使用最广泛的,eclipse新建工程时,都默认带有了。
Android Support v7: 这个包是为了考虑照顾2.1及以上版本而设计的,但不包含更低,故如果不考虑1.6,我们可以采用再加上这个包,另外注意,v7是要依赖v4这个包的,即,两个得同时被包含。
Android Support v13 :这个包的设计是为了android 3.2及更高版本的,一般我们都不常用,平板开发中能用到。
如果我们想要创建Notification,需要先指定一个
可以尝试一下,如果不设置largeicon,而直接设置smallIcon,此时的显示效果是这样的:
如果我们想添加一些更加丰富的扩展信息,可以通过设置builder属性来实现。对于Notification常用的方法和参数,我们这里简单介绍一下:
如果你想点击完notification后,该通知自动消失,那么你就需要调用setAutoCancel(boolean b)这个方法,并且将其设为true,如果你想让你的通知栏常驻,用户无法滑动删除,也不能通过手机的清除键 删除,类似于墨迹天气等app的通知栏,那么你可以设置setOngoing方法,也设为true,这样,通知栏只能通过代码调用cancel方法才能消失,很霸道地,有木有!设置优先级的方法,就是调用setPriority(int
p)。另外,当启动通知栏的时候,我们常常可以在手机最上端的Status Bar上面,会闪现一段提示语,用来提醒用户,这段提示语具体显示的文字,就是靠setTicker() 这个方法来实现的。除此之外,我们还可以设置,推送通知时的铃声、震动效果,闪光灯效果等等,需要注意一点的是,设置通知的铃声,除去调用系统自带的外,还有两种方式,分别是调用SD卡中的声音文件和项目工程自带的声音文件,这两种方式都需要用到Uri的地址,具体如何获取这两种的Uri。
自从Android4.1之后,谷歌引入了一种新的样式,叫做Big View,效果就是相对于传统的Notification,它的显示区域更大,显示的内容也更多一些。关于Big View,谷歌支持了三种模式,分别是:Big
picture style 和 Big text style 还有 Inbox style
1.BigPictureStyle
首先我们实例化一个NotificationCompat.BigPictureStyle,然后读取要展示的图片资源,调用picStyle.bigPicture(bitmap)这个方法设置图片,最后调用notifyBuilder的mBuilder.setStyle(picStyle)方法,设置好BIG
VIEW的样式,就OK了,代码简单的令人发指,我就不多解释了,大家参考上面的示例代码即可。
2.InboxStyle
为了在Notification中使用扩展的布局,需要调用
问题来了,扩展布局的兼容性怎么搞?官方建议我们,创建Notification要使用
还有,我们要在Notification中处理一些事件的时候,Google建议我们,将这些动作放在与Notification关联的Activity中去完成。怎样关联Activity,我们继续往下看...
刚刚提到的自动跳转页面的功能,看似很简单的一个逻辑,其实也包含了各种逻辑处理情况,其中最主要的是有两种业务场景:
一我们通过Notification和app图标进入邮件应用,这两种进入方式,在按返回键时,效果是相同的。即我们点击Notification,系统会为我们打开最新收到的邮件,当我们看完邮件后,按返回键,我们并不会马上回到手机桌面的主屏上,而是先返回到收件箱界面,然后再返回到邮件APP的主界面,然后再返回到手机桌面的主屏上,它是按照邮件APP的页面队列返回的。
二:当我们收到新邮件的通知后,我们点击打开新收到的邮件,当我们阅读完之后,我们想要点击返回键,立刻返回到我们刚刚所处的界面,继续进行刚才还在进行的任务。
这里就涉及到任务队列的问题。
1.定义与Notification关联的Activity,配置文件:
创建任务栈,调用
我们利用这段代码,首先实例化了一个TaskStackBuilder,然后调用addParentStack()和addNextIntent(),设置它的返回堆栈和跳转页面,跳转页面的Intent很好理解,跟大家平时的设置方式是一样的,那么它的返回堆栈是如何控制的呢?这就需要上面xml配置文件的配置了,大家请看,在上面的配置文件中,一共有三个Activity,分别是MainActivity,OtherActivity,和NotifyRegularActivity,其中NotifyRegularActivity就是我们点击通知栏后,要自动跳转的界面。在配置文件当中,
我们给后面两个Activity,设置了这么一个属性android:parentActivityName,它指的就是该activity的返回路径,因为刚刚我们在调用addParentStack()这个方法的时候,设置的参数是NotifyRegularActivity.class所以根据上面配置文件的配置内容,那么它的返回堆栈的顺序就是:
ResultActivity —> SecondActivity —> MainActivity
需要注意的是,为了向下兼容版本,我们在设置android:parentActivityName这个属性的时候,还需要在配置文件中,为每个Activity进行如下设置:
这里有两点需要注意一下:
1:PendingIntent.FLAG_UPDATE_CURRENT这个参数一般有四种选择分别是:
FLAG_CANCEL_CURRENT:如果构建的PendingIntent已经存在,则取消前一个,重新构建一个。
FLAG_NO_CREATE:如果前一个PendingIntent已经不存在了,将不再构建它。
FLAG_ONE_SHOT:表明这里构建的PendingIntent只能使用一次。
FLAG_UPDATE_CURRENT:如果构建的PendingIntent已经存在,那么系统将不会重复创建,只是把之前不同的传值替换掉。
如果没有特殊要求的话,我们常常会使用FLAG_UPDATE_CURRENT这个参数来构造PendingIntent,但是这样常常会引发第二个问题,什么问题呢?呵呵~
2:如上所述我们使用FLAG_UPDATE_CURRENT这个参数后,常常会发现,我们点击通知栏后,系统没有响应,时灵时不灵的,很是忧郁,这是为什么呢?原来使用FLAG_UPDATE_CURRENT这个参数后,系统不会重新创建新的PendingIntent,这样一来,如果你传递的Intent的 extra参数没有变化的话,那么系统就会认为你没有发送新的PendingIntent,这样就不会重新响应你的点击事件。一般情况下,为了能够区分每次的PendingIntent不一样,我们常常会在构造Intent的时候,设置不同的Action或者Extra值,这样一来,即使是使用FLAG_UPDATE_CURRENT这个参数,系统也会因为传值参数的变化而去响应每次的点击跳转事件。不过这种解决方法还是有些麻烦,有时候,我们根本不需要传递额外的Aciton或者参数值,这该怎么办呢?哈哈,解决代码已经在上面的代码中写出来了,在stackBuilder.getPendingIntent(requestCode,
PendingIntent.FLAG_UPDATE_CURRENT)这个方法中,我们注意到第一个参数,这里,我们只要为这个参数设置一个独一无二的标识,那么刚刚提到的点击无响应的问题就迎刃而解了,我平时的设置办法就是利用这段代码:
上面的讲解,我们就可以解决刚刚讨论的第一种情形了,下面我们来说一下,第二种情形,也就是点击返回键后,直接返回刚刚任务所处的界面,看看这个如何实现。来,看代码:
继续看配置文件的设置:
在代码中,我们设置ResultActivity为我们要跳转的界面,然后在xml的配置文件中,我们重点设置了这三个属性:android:excludeFromRecents="true",android:launchMode="singleTask",android:taskAffinity="",第一个属性的设置,是将该界面从最近任务栏当中移除,防止用户通过最近任务栏而进入到该界面,这样一来,只能通过通知来的点击来进入。第二种属性的设置就很常见了,是为了防止该界面存在的情况下,重复创建该Activity,第三属性是为了配置代码中的这段来设置的:
这样的作用是为此次跳转界面的行为重新分配一个任务堆栈,而不从属于其它的任务堆栈,这样的话,当我们点击返回键后,就可以直接返回到刚刚用户所处的任务界面了。由于这里我们不再使用TaskStackBuilder,所以最后需要调用PendingIntent.getActivity(this, requestCode,notifyIntent,PendingIntent.FLAG_UPDATE_CURRENT)这个方法来构造一个PendingIntent,然后赋值给notifyBuilder。这样,刚刚讨论过的第二种情形,我们就可以解决了,相比较第一种来说,这种解决方式更为简洁,不过处理的业务逻辑也不一样,大家斟酌而定。
最后是Notification与Service与Broadcast,其实万变不离其宗,Activity搞定了,Service与Broadcast是同样的道理,区别仅仅在Intent上,有木有一种怅然若失的赶脚,原来就是酱紫...下面是PendingIntent的构造方法。
有时候我们会遇到一种看似很不合理的需求,笔者曾经就遇到过这样一种情形,具体逻辑就不说了,主要是区分Notification的滑动和点击事件,想了很久,最后找了一种讨巧的方法,这里简单提一下:
比如我们在滑动和点击Notification时,处理不同的业务逻辑,业务逻辑的处理,我们放在Service中处理。问题来了,怎么区分呢?我是从Intent中下手的,通过区别这两个Intent来实现,在NotificationCompat.Builder中有几个方法,
setContentIntent()
setDeleteIntent()
这样在Service的onStartCommand()中通过Intent,就能达到区分的效果了...
第一个参数指的的是进度的总长度,第二个参数是目前进行的长度,然后将第三个参数设为false,我们可以看到的效果就如下图:
我们可以看见进度条的确切位置和进度情况。还有一种使用方法是这样的:
最后,当我们的任务完成后,我们需要取消进度条的显示,这时候我们需要调用如下方法:
设置一个任务完成后的文本描述,然后将setProgress的前两个参数都设为0,最后一个参数设为false,这样进度条就不会在通知栏上面显示了,效果图:
自定义Notification布局的app有很多,比如像墨迹天气,Clean Master等等,利用自定义布局,将用户所需信息和快捷功能,多样化的展示在通知栏上面,给大家看一下Clean Master的截图:
其实要是实现这种自定义布局的Notification,非常简单,我们这就给大家展示代码设置和布局配置:
先看看java代码:
最后的最后,需要再给大家介绍两个方法,那就是通过代码来取消Notification,咱不能只管杀不管埋啊,哈哈~
cancelNotificationManager.cancel(NOTIFY_ID)这个方法是根据之前发布通知时,分配的ID号,来取消对应的通知栏。
cancelNotificationManager.cancelAll()这个方法是取消所有之前发布过的通知栏,比较暴力一点哈。
使用了最新的Material Design 主题;
Notification可以在锁屏的情况下使用,当然,一些敏感的提醒,依旧被隐藏。
当设备正在被使用时,依旧可以接收称为heads-up notifications 的优先级较高的提醒。
Cloud-synced notifications:云同步,当你在一台设备上关闭了提醒,在你其他的Android设备上,提醒也会被关闭。
在Android5.0 API Level21 如下图中,在屏幕顶端的浮动的窗口,又叫Heads-up Notification,当应用全屏显示的时候,这时使用heads-up notification就比较合适了,这个Notification具有很高的优先级,可以使用系统的铃声和振动效果。
在Android5.0中,Notification也可以出现在锁屏的情况中,这样应用就可以进行一些播放器的操作,可以通过
来控制Notification在锁屏情况下的显示和隐藏。Google给我们提供了三个备选参数,分别是:
在Android5.0之前,我们在Notification中控制音乐,主要是通过RemoteControlClient,5.0之后,取而代之的是
随着扁平化主题成为流行,Android5.0上的主题样式也让人更加舒服,值得一提的是Android终于引入了圆角图片的显示,再也不用自定义控件来替代了,此外还有一些更加丰富的扩展布局。提醒功能更加合理和人性化,以前总感觉Android与Apple的界面展示,用户体验上,Android总是稍逊一筹,相信随着Android5.0的占有率不断增加,这一感觉会有很大的改观...
参考文档:
http://developer.android.com/design/patterns/notifications_k.html
http://developer.android.com/design/patterns/notifications.html
http://blog.csdn.net/xy_nyle/article/details/19853591
这个notification一般用在电话,短信,邮件,闹钟铃声,在手机的状态栏上就会出现一个小图标,提示用户处理这个通知,这时手从上方滑动状态栏就可以展开并处理这个快讯。已添加的Notification.Builder,使其更容易构建通知。
Notification是一种让你的应用程序在没有开启情况下或在后台运行警示用户。它是看不见的程序组件(Broadcast Receiver,Service和不活跃的Activity)警示用户有需要注意的事件发生的最好途径。
1. Notification 在Android4.4 及之前
1.1 基本布局
系统提供的Notification的基本布局方式,如图,主要包括5个部分:Icon,Title,Message,Timestamp,Secondary Icon,1.2 扩展布局
随着Android不断的推陈出新,Notification的布局方式也越来越多样,提示的信息也越来越详细。你可以使用Notification来显示多行的文本信息,甚至能显示图片的预览图,有些时候,你甚至不需要打开系统的信息,直接在Notification中,就可以完全完成阅读。Android提供了两种扩展的布局
模板(文本和图片,如图所示)。
1.3 Action
Notification还诸如在Notification的底部添加一些扩展性的操作,Android的Notification是android系统中很重要的一个机制,产品人员常常利用通知栏的方式,跟用户进行弱沟通。拥有推送通知的app要比没有此类功能的app活跃率要高很多。另外类似于墨迹天气,清理大师等app,也会将通知栏常驻,利用自定义的布局,方便用户及时快捷的查看所需的信息和使用快捷的功能。所以Notification的使用,也在开发当中,使用的越来越频繁,用户体验也非常不错。1.4 正确设置和管理Notification的优先级
从Android 4.2(Jelly Bean)开始,Android就开始支持对Notification设置优先级,通过设置flags,可以让你的应用在其他的应用之前,让用户看到你的提示信息。Notification 支持5个级别,分别是:Max,High,Default,Low,Min,级别也是依次降低。Max:提示一些重要并且紧急的通知,主要适用于,在开始某项任务前,必须要解决和处理的提示信息。
High:提示一些比较重要的信息,比如短信等聊天内容。
Default:系统默认的级别,在同是Default级别的其他提醒,不会覆盖掉当前的提醒。
Low:提示用户,一些紧迫性较低的提醒。
Min:最低级别,只有用户在滑下通知栏时,才会显示提示信息,否则是不会出现在状态栏中。比如一些天气信息的提醒。
1.5 Notification的堆栈处理
同一应用的两个相同的提示信息,进行了堆栈处理,比如途中两条相同的短信提醒,并不是显示两个提醒。而是提示有两条信息。当然,也可以使用一些扩展性的布局,通过堆栈的方式,获取更好的用户体验。
1.6 使用上的建议
官方文档上,建议我们的app添加设置选项,让用户来控制是否显示提示信息,是否有声音,震动的提示,推荐使用Android提供的图标和显示样式,尤其,这是我们在设计app时很容易出现的问题,总想与众不同,结果用户体验很差。
官方文档中,推荐我们调用一些LED提醒,这样在屏幕关闭的情况下,比如通过呼吸灯的颜色,来提醒用户。在设计app时,提示信息要友好,一些不必要的,
不重要的提醒,就不要骚扰用户啦,小心用户烦了,分分钟干掉你……
2.Notification 的创建与使用
2.1 创建一个简单的Notification
很久以前,我们想使用一个Notification,需要用到下面的代码Notification notification=new Notification(notificationIcon, notificationTitle, when); notification.defaults=Notification.DEFAULT_ALL; Intent intent=new Intent(MainActivity.this,SecondActivity.class); PendingIntent pendingIntent=PendingIntent.getActivity(MainActivity.this, 0, intent, 0); notification.setLatestEventInfo(this,"测试展开title", "测试展开内容",pendingIntent);
细心的你可能会发现,这种写法已经过时,随着Google的推新除陈,在v4的包中准确的说是在Android3.0(API11),引入了一种更加优雅的写法,先脑补一下v4,v7,v13:
Android Support v4: 这个包是为了照顾1.6及更高版本而设计的,这个包是使用最广泛的,eclipse新建工程时,都默认带有了。
Android Support v7: 这个包是为了考虑照顾2.1及以上版本而设计的,但不包含更低,故如果不考虑1.6,我们可以采用再加上这个包,另外注意,v7是要依赖v4这个包的,即,两个得同时被包含。
Android Support v13 :这个包的设计是为了android 3.2及更高版本的,一般我们都不常用,平板开发中能用到。
如果我们想要创建Notification,需要先指定一个
NotificationCompat.Builder对象,我们可以通过Builder来设置要创建的Notification的UI和一些动作,通过
NotificationCompat.Builder.build()创建一个Notification实例,通过
NotificationManager.notify()完成一个Notification的显示。
mNotifyManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); mBuilder = new NotificationCompat.Builder(MainActivity.this); mBuilder.notify();但是这样,Notification并没有任何的显示信息,一个完整的Notification必须要包含以下内容:
mBuilder.setSmallIcon(R.drawable.ic_launcher); mBuilder.setContentTitle("推荐内容"); mBuilder.setContentText("这是一条Notification的测试信息....");
可以尝试一下,如果不设置largeicon,而直接设置smallIcon,此时的显示效果是这样的:
如果我们想添加一些更加丰富的扩展信息,可以通过设置builder属性来实现。对于Notification常用的方法和参数,我们这里简单介绍一下:
如果你想点击完notification后,该通知自动消失,那么你就需要调用setAutoCancel(boolean b)这个方法,并且将其设为true,如果你想让你的通知栏常驻,用户无法滑动删除,也不能通过手机的清除键 删除,类似于墨迹天气等app的通知栏,那么你可以设置setOngoing方法,也设为true,这样,通知栏只能通过代码调用cancel方法才能消失,很霸道地,有木有!设置优先级的方法,就是调用setPriority(int
p)。另外,当启动通知栏的时候,我们常常可以在手机最上端的Status Bar上面,会闪现一段提示语,用来提醒用户,这段提示语具体显示的文字,就是靠setTicker() 这个方法来实现的。除此之外,我们还可以设置,推送通知时的铃声、震动效果,闪光灯效果等等,需要注意一点的是,设置通知的铃声,除去调用系统自带的外,还有两种方式,分别是调用SD卡中的声音文件和项目工程自带的声音文件,这两种方式都需要用到Uri的地址,具体如何获取这两种的Uri。
2.2 使用扩展布局的Notification
自从Android4.1之后,谷歌引入了一种新的样式,叫做Big View,效果就是相对于传统的Notification,它的显示区域更大,显示的内容也更多一些。关于Big View,谷歌支持了三种模式,分别是:Big
picture style 和 Big text style 还有 Inbox style
1.BigPictureStyle
首先我们实例化一个NotificationCompat.BigPictureStyle,然后读取要展示的图片资源,调用picStyle.bigPicture(bitmap)这个方法设置图片,最后调用notifyBuilder的mBuilder.setStyle(picStyle)方法,设置好BIG
VIEW的样式,就OK了,代码简单的令人发指,我就不多解释了,大家参考上面的示例代码即可。
Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_action_search); NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this).setSmallIcon(R.drawable.ic_launcher) .setLargeIcon(icon) .setContentTitle("Big picture..."); BigPictureStyle pictureStyle = new NotificationCompat.BigPictureStyle(); Bitmap bigPic = BitmapFactory.decodeResource(getResources(), R.drawable.bigpic); pictureStyle.bigPicture(bigPic); mBuilder.setStyle(pictureStyle); NotificationManager mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); mNotificationManager.notify(NOTIFY_ID, mBuilder.build());
2.InboxStyle
为了在Notification中使用扩展的布局,需要调用
Builder.setStyle();有一点值得注意的是,这个扩展布局的效果,只有在Android4.1上有效,而且也并不是所有的机型上都能产生这个效果,在小米2s上测试,就没有这个扩展的效果。
Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_action_search); NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this).setSmallIcon(R.drawable.ic_launcher).setLargeIcon(icon).setNumber(10); NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle(); String[] events = new String[] {"Kobe (google+) first event...", "James message second event...", "third event..."}; // Sets a title for the Inbox in expanded layout // Moves events into the expanded layout for (int i = 0; i < events.length; i++) { inboxStyle.addLine(events[i]); } // Moves the expanded layout object into the notification object. inboxStyle.setBigContentTitle("3 Messages:"); inboxStyle.setSummaryText("sguotao@gmail.com"); mBuilder.setStyle(inboxStyle); // Issue the notification here. NotificationManager mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); // mId allows you to update the notification later on. mNotificationManager.notify(NOTIFY_ID, mBuilder.build());
问题来了,扩展布局的兼容性怎么搞?官方建议我们,创建Notification要使用
NotificationCompat及
NotificationCompat的子类,特别是
NotificationCompat.Builder.
还有,我们要在Notification中处理一些事件的时候,Google建议我们,将这些动作放在与Notification关联的Activity中去完成。怎样关联Activity,我们继续往下看...
2.3 Notification与Activity、Service、Broadcast及点击事件等
我们经常遇到这样的情形,当我们点击了一个notification后,就会自动打开一个页面,展示出信息来源的具体页面,接下来,我们就针对这种情况,来看看代码是如何控制的。刚刚提到的自动跳转页面的功能,看似很简单的一个逻辑,其实也包含了各种逻辑处理情况,其中最主要的是有两种业务场景:
一我们通过Notification和app图标进入邮件应用,这两种进入方式,在按返回键时,效果是相同的。即我们点击Notification,系统会为我们打开最新收到的邮件,当我们看完邮件后,按返回键,我们并不会马上回到手机桌面的主屏上,而是先返回到收件箱界面,然后再返回到邮件APP的主界面,然后再返回到手机桌面的主屏上,它是按照邮件APP的页面队列返回的。
二:当我们收到新邮件的通知后,我们点击打开新收到的邮件,当我们阅读完之后,我们想要点击返回键,立刻返回到我们刚刚所处的界面,继续进行刚才还在进行的任务。
这里就涉及到任务队列的问题。
1.定义与Notification关联的Activity,配置文件:
<activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".ResultActivity" android:parentActivityName=".MainActivity"> <meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".MainActivity"/>2.创建打开Activity的Intent,调用
TaskStackBuilder.create().
创建任务栈,调用
addParentStack() 将Activity添加到任务栈。
<activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".ResultActivity" android:parentActivityName=".SecondActivity" > <meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".SecondActivity" /> </activity> <activity android:name=".SecondActivity" android:parentActivityName=".MainActivity" > <meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".MainActivity" > </meta-data> </activity>
我们利用这段代码,首先实例化了一个TaskStackBuilder,然后调用addParentStack()和addNextIntent(),设置它的返回堆栈和跳转页面,跳转页面的Intent很好理解,跟大家平时的设置方式是一样的,那么它的返回堆栈是如何控制的呢?这就需要上面xml配置文件的配置了,大家请看,在上面的配置文件中,一共有三个Activity,分别是MainActivity,OtherActivity,和NotifyRegularActivity,其中NotifyRegularActivity就是我们点击通知栏后,要自动跳转的界面。在配置文件当中,
我们给后面两个Activity,设置了这么一个属性android:parentActivityName,它指的就是该activity的返回路径,因为刚刚我们在调用addParentStack()这个方法的时候,设置的参数是NotifyRegularActivity.class所以根据上面配置文件的配置内容,那么它的返回堆栈的顺序就是:
ResultActivity —> SecondActivity —> MainActivity
需要注意的是,为了向下兼容版本,我们在设置android:parentActivityName这个属性的时候,还需要在配置文件中,为每个Activity进行如下设置:
<meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".MainActivity" > </meta-data>具体的value的指向,就需要你自己设定了,总之,它指向了该activity的返回页面。对TaskStackBuilder设置完成之后,我们再通过下面的代码获取PendingIntent,,然后赋值给notifyBuilder即可:
// 当设置下面PendingIntent.FLAG_UPDATE_CURRENT这个参数的时候,常常使得点击通知栏没效果,你需要给notification设置一个独一无二的requestCode int requestCode = (int) SystemClock.uptimeMillis(); PendingIntent resultPendingIntent = stackBuilder.getPendingIntent( requestCode, PendingIntent.FLAG_UPDATE_CURRENT); notifyBuilder.setContentIntent(resultPendingIntent);
这里有两点需要注意一下:
1:PendingIntent.FLAG_UPDATE_CURRENT这个参数一般有四种选择分别是:
FLAG_CANCEL_CURRENT:如果构建的PendingIntent已经存在,则取消前一个,重新构建一个。
FLAG_NO_CREATE:如果前一个PendingIntent已经不存在了,将不再构建它。
FLAG_ONE_SHOT:表明这里构建的PendingIntent只能使用一次。
FLAG_UPDATE_CURRENT:如果构建的PendingIntent已经存在,那么系统将不会重复创建,只是把之前不同的传值替换掉。
如果没有特殊要求的话,我们常常会使用FLAG_UPDATE_CURRENT这个参数来构造PendingIntent,但是这样常常会引发第二个问题,什么问题呢?呵呵~
2:如上所述我们使用FLAG_UPDATE_CURRENT这个参数后,常常会发现,我们点击通知栏后,系统没有响应,时灵时不灵的,很是忧郁,这是为什么呢?原来使用FLAG_UPDATE_CURRENT这个参数后,系统不会重新创建新的PendingIntent,这样一来,如果你传递的Intent的 extra参数没有变化的话,那么系统就会认为你没有发送新的PendingIntent,这样就不会重新响应你的点击事件。一般情况下,为了能够区分每次的PendingIntent不一样,我们常常会在构造Intent的时候,设置不同的Action或者Extra值,这样一来,即使是使用FLAG_UPDATE_CURRENT这个参数,系统也会因为传值参数的变化而去响应每次的点击跳转事件。不过这种解决方法还是有些麻烦,有时候,我们根本不需要传递额外的Aciton或者参数值,这该怎么办呢?哈哈,解决代码已经在上面的代码中写出来了,在stackBuilder.getPendingIntent(requestCode,
PendingIntent.FLAG_UPDATE_CURRENT)这个方法中,我们注意到第一个参数,这里,我们只要为这个参数设置一个独一无二的标识,那么刚刚提到的点击无响应的问题就迎刃而解了,我平时的设置办法就是利用这段代码:
int requestCode = (int) SystemClock.uptimeMillis();获取发布通知时的时间,将它作为requestCode,这样就可以避免这些问题了。不过如果你要是使用FLAG_CANCEL_CURRENT这个参数的话,就会每次都创建一个新的,那么刚刚提到的这两个问题,也都不存在了,具体怎么用,看你实际的业务要求了。
上面的讲解,我们就可以解决刚刚讨论的第一种情形了,下面我们来说一下,第二种情形,也就是点击返回键后,直接返回刚刚任务所处的界面,看看这个如何实现。来,看代码:
Intent notifyIntent = new Intent(this, NotifySpecialActivity.class); notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); // Creates the PendingIntent // 当设置下面PendingIntent.FLAG_UPDATE_CURRENT这个参数的时候,常常使得点击通知栏没效果,你需要给notification设置一个独一无二的requestCode int requestCode = (int) SystemClock.uptimeMillis(); PendingIntent pendIntent = PendingIntent.getActivity(this, requestCode, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT); notifyBuilder.setContentIntent(pendIntent);
继续看配置文件的设置:
<activity android:name=".ResultActivity" android:excludeFromRecents="true" android:launchMode="singleTask" android:parentActivityName=".SecondActivity" android:taskAffinity="" > </activity>
在代码中,我们设置ResultActivity为我们要跳转的界面,然后在xml的配置文件中,我们重点设置了这三个属性:android:excludeFromRecents="true",android:launchMode="singleTask",android:taskAffinity="",第一个属性的设置,是将该界面从最近任务栏当中移除,防止用户通过最近任务栏而进入到该界面,这样一来,只能通过通知来的点击来进入。第二种属性的设置就很常见了,是为了防止该界面存在的情况下,重复创建该Activity,第三属性是为了配置代码中的这段来设置的:
notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
这样的作用是为此次跳转界面的行为重新分配一个任务堆栈,而不从属于其它的任务堆栈,这样的话,当我们点击返回键后,就可以直接返回到刚刚用户所处的任务界面了。由于这里我们不再使用TaskStackBuilder,所以最后需要调用PendingIntent.getActivity(this, requestCode,notifyIntent,PendingIntent.FLAG_UPDATE_CURRENT)这个方法来构造一个PendingIntent,然后赋值给notifyBuilder。这样,刚刚讨论过的第二种情形,我们就可以解决了,相比较第一种来说,这种解决方式更为简洁,不过处理的业务逻辑也不一样,大家斟酌而定。
最后是Notification与Service与Broadcast,其实万变不离其宗,Activity搞定了,Service与Broadcast是同样的道理,区别仅仅在Intent上,有木有一种怅然若失的赶脚,原来就是酱紫...下面是PendingIntent的构造方法。
有时候我们会遇到一种看似很不合理的需求,笔者曾经就遇到过这样一种情形,具体逻辑就不说了,主要是区分Notification的滑动和点击事件,想了很久,最后找了一种讨巧的方法,这里简单提一下:
比如我们在滑动和点击Notification时,处理不同的业务逻辑,业务逻辑的处理,我们放在Service中处理。问题来了,怎么区分呢?我是从Intent中下手的,通过区别这两个Intent来实现,在NotificationCompat.Builder中有几个方法,
setContentIntent()
setDeleteIntent()
这样在Service的onStartCommand()中通过Intent,就能达到区分的效果了...
2.4 Notification中显示进度条
Notificaton在平时的产品设计中,常常用来显示跟网络交互的进度,我们常常的做法是在通知栏上面,显示一个进度条,用来更新交互的进度,这个的实现方式很简单,主要依赖于mBuilder.setProgress()这个方法,具体的做法可以参考下面的代码:final NotificationManager mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); final NotificationCompat.Builder mBuilder = new NotificationCompat.Builder( this); mBuilder.setContentTitle("Picture Download") .setContentText("Download in progress") .setSmallIcon(R.drawable.small); new Thread(new Runnable() { @Override public void run() { int incr; for (incr = 0; incr <= 100; incr += 5) { // mBuilder.setProgress(100, incr, false); mBuilder.setProgress(0, 0, true); mNotifyManager.notify(NOTIFY_ID, mBuilder.build()); try { Thread.sleep(1 * 1000); } catch (InterruptedException e) { } } mBuilder.setContentText("Download complete").setProgress(0, 0, false); mNotifyManager.notify(NOTIFY_ID, mBuilder.build()); } } // Starts the thread by calling the run() method in its Runnable ).start(); }在代码中,我们开启了一个线程,里面进行20次for循环,每次循环都会调用setProgress这个方法,因为每次调用的NOTIFY_ID都是相同的,所以系统会根据这个ID来更新notification的进度而不会重新创建一个新的Notification。setProgress这个方法一共有两种方法,一种是这样的:
mBuilder.setProgress(100, incr, false);
第一个参数指的的是进度的总长度,第二个参数是目前进行的长度,然后将第三个参数设为false,我们可以看到的效果就如下图:
我们可以看见进度条的确切位置和进度情况。还有一种使用方法是这样的:
mBuilder.setProgress(0, 0, true);将前两个参数都设为0,然后将最后这个参数设为true,这样的进度条效果是一种连续模糊的,适合进行时间不确定的网络连接,效果图如下:
最后,当我们的任务完成后,我们需要取消进度条的显示,这时候我们需要调用如下方法:
mBuilder.setContentText("Download complete").setProgress(0, 0, false);
设置一个任务完成后的文本描述,然后将setProgress的前两个参数都设为0,最后一个参数设为false,这样进度条就不会在通知栏上面显示了,效果图:
2.5 自定义布局的Notification
最后再给大家介绍一种自定义 Notification布局的用法。Framework层允许我们通过自定义布局的方式,给Notification设置。主要是通过RemoteViews对象。当然系统也对自定义的布局的高度给出了限制,普通的Notification的高度是64dp,Expanded类型的高度是256dp,
自定义Notification布局的app有很多,比如像墨迹天气,Clean Master等等,利用自定义布局,将用户所需信息和快捷功能,多样化的展示在通知栏上面,给大家看一下Clean Master的截图:
其实要是实现这种自定义布局的Notification,非常简单,我们这就给大家展示代码设置和布局配置:
先看看java代码:
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this); RemoteViews remoteView = new RemoteViews(getPackageName(),R.layout.remote); remoteView.setTextViewText(R.id.text, "Custom Text"); remoteView.setTextViewText(R.id.btn, "Custom Button"); remoteView.setImageViewResource(R.id.image, R.drawable.ic_launcher); Intent notifyIntent = new Intent(this, NotifySpecialActivity.class); notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); // Creates the PendingIntent // 当设置下面PendingIntent.FLAG_UPDATE_CURRENT这个参数的时候,常常使得点击通知栏没效果,你需要给notification设置一个独一无二的requestCode int requestCode = (int) SystemClock.uptimeMillis(); PendingIntent pendIntent = PendingIntent.getActivity(this, requestCode, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT); remoteView.setOnClickPendingIntent(R.id.btn, pendIntent); mBuilder.setSmallIcon(R.drawable.small); mBuilder.setContent(remoteView); NotificationManager mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mNotifyManager.notify(NOTIFY_ID, mBuilder.build());再来看看xml布局文件是什么样的:
<?xml version="1.0" encoding="UTF-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="64dp" > <ImageView android:id="@+id/image" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_alignParentLeft="true" android:gravity="center" /> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_centerInParent="true" android:gravity="center" /> <Button android:id="@+id/btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:gravity="center" /> </RelativeLayout>我们首先利用下面这行代码去解析上面的布局文件
RemoteViews remoteView = new RemoteViews(getPackageName(),R.layout.remote);然后根据每个控件的id号进行资源设置:
remoteView.setTextViewText(R.id.text, "Custom Text"); remoteView.setTextViewText(R.id.btn, "Custom Button"); remoteView.setImageViewResource(R.id.image, R.drawable.ic_launcher);我们也可以为这些控件单独设置点击事件,比如设置Button的点击事件:
remoteView.setOnClickPendingIntent(R.id.btn, pendIntent);上面第二个参数pendingIntent的获取,在之前的讲解中,已经介绍了好几种方式了,这里我们随便选择一种来实现了:
Intent notifyIntent = new Intent(this, NotifySpecialActivity.class); notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); // Creates the PendingIntent // 当设置下面PendingIntent.FLAG_UPDATE_CURRENT这个参数的时候,常常使得点击通知栏没效果,你需要给notification设置一个独一无二的requestCode int requestCode = (int) SystemClock.uptimeMillis(); PendingIntent pendIntent = PendingIntent.getActivity(this, requestCode, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT);最后,我们要调用下面这段代码,将自定义的RemoteView设置给notifyBuilder,然后调用发送通知的方法就OK了。
mBuilder.setContent(remoteView);
最后的最后,需要再给大家介绍两个方法,那就是通过代码来取消Notification,咱不能只管杀不管埋啊,哈哈~
NotificationManager cancelNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); cancelNotificationManager.cancel(NOTIFY_ID); cancelNotificationManager.cancelAll();
cancelNotificationManager.cancel(NOTIFY_ID)这个方法是根据之前发布通知时,分配的ID号,来取消对应的通知栏。
cancelNotificationManager.cancelAll()这个方法是取消所有之前发布过的通知栏,比较暴力一点哈。
3.Notification 在Android5.0 的新特性
在Android5.0上,Notification无论是从功能上,视觉上,结构上都做了重大的改进。使用了最新的Material Design 主题;
Notification可以在锁屏的情况下使用,当然,一些敏感的提醒,依旧被隐藏。
当设备正在被使用时,依旧可以接收称为heads-up notifications 的优先级较高的提醒。
Cloud-synced notifications:云同步,当你在一台设备上关闭了提醒,在你其他的Android设备上,提醒也会被关闭。
在Android5.0 API Level21 如下图中,在屏幕顶端的浮动的窗口,又叫Heads-up Notification,当应用全屏显示的时候,这时使用heads-up notification就比较合适了,这个Notification具有很高的优先级,可以使用系统的铃声和振动效果。
在Android5.0中,Notification也可以出现在锁屏的情况中,这样应用就可以进行一些播放器的操作,可以通过
setVisibility()
来控制Notification在锁屏情况下的显示和隐藏。Google给我们提供了三个备选参数,分别是:
VISIBILITY_PUBLIC显示Notification完整的内容
VISIBILITY_SECRET不显示任何内容
VISIBILITY_PRIVATE只显示基本的信息,比如图标和标题,隐藏了内容
在Android5.0之前,我们在Notification中控制音乐,主要是通过RemoteControlClient,5.0之后,取而代之的是
Notification.MediaStyle,示例代码
Notification notification = new Notification.Builder(context) // Show controls on lock screen even when user hides sensitive content. .setVisibility(Notification.VISIBILITY_PUBLIC) .setSmallIcon(R.drawable.ic_stat_player) // Add media control buttons that invoke intents in your media service .addAction(R.drawable.ic_prev, "Previous", prevPendingIntent) // #0 .addAction(R.drawable.ic_pause, "Pause", pausePendingIntent) // #1 .addAction(R.drawable.ic_next, "Next", nextPendingIntent) // #2 // Apply the media style template .setStyle(new Notification.MediaStyle() .setShowActionsInCompactView(1 /* #1: pause button */) .setMediaSession(mMediaSession.getSessionToken()) .setContentTitle("Wonderful music") .setContentText("My Awesome Band") .setLargeIcon(albumArtBitmap) .build();
随着扁平化主题成为流行,Android5.0上的主题样式也让人更加舒服,值得一提的是Android终于引入了圆角图片的显示,再也不用自定义控件来替代了,此外还有一些更加丰富的扩展布局。提醒功能更加合理和人性化,以前总感觉Android与Apple的界面展示,用户体验上,Android总是稍逊一筹,相信随着Android5.0的占有率不断增加,这一感觉会有很大的改观...
参考文档:
http://developer.android.com/design/patterns/notifications_k.html
http://developer.android.com/design/patterns/notifications.html
http://blog.csdn.net/xy_nyle/article/details/19853591
相关文章推荐
- 体验Android
- Android源码各个击破之-系统属性
- Study on Android【一】--概述
- 机器人Android在呼唤!
- Google Android IDE-eclipse plugin 预览
- 为什么是java,google的android策略
- Android Developer Challenge
- Google Calling: Inside Android, the gPhone SDK
- Android程序开发初级教程(三)
- Android程序开发初级教程(二)
- 什么是google的Android操作系统?
- Google手机操作系统Android应用开发入门
- Android程序开发初级教程(一)
- Android 入门开发指南之一 -- Android简介
- Android 原代码下载路径
- 体验Android:个人所得税计算器 含源码
- Android判断字符串中是否含字母、中文或数字
- 体验Android
- Google 1000万美元重奖Android开发者
- Android安装以及Eclipse插件(Google Android) ,在Android 中google搜索 博客园界面