分类:C#、Android、VS2015;
创建日期:2016-02-29
一、如何向用户发出通知
1、使用Toast通知用户
前台任务中的通知(Notifications)一般用于长时间显示用户正在关注的服务的消息。但有时候,我们可能只希望将最终结果短暂地显示出来,比如告诉用户文件已下载完毕等,此时可以用Toast告诉用户。
由于服务是在后台运行的,所以可以在服务中用Handler来实现Toast的显示。例如:
var myHandler = new Handler ();
...
myHandler.Post(() =>
{
Toast.MakeText (this, "Message from demo service", ToastLength.Long).Show();
});
除了用Handler实现以外,也可以在服务中用其他的办法来实现,例如:
Android.App.Application.SynchronizationContext.Post(()=>
{
Toast.MakeText (this, "Message from demo service", ToastLength.Long).Show();
});
或者:
System.Threading.SynchronizationContext.Current.Post(()=>
{
Toast.MakeText (this, "Message from demo service", ToastLength.Long).Show();
});
2、使用Notification通知用户
从前面的介绍中我们已经明白,安卓的服务组件(Android Service,后面章节再细讲)本来是在后台运行的,可是,用户可能希望关注它正在执行的状态或者当前的结果,换言之,这个服务的状态监控非常重要。此时就可以将“监控”作为前台任务来处理,即:用“通知”的办法及时告诉用户后台服务当前正在运行的情况。
下面的示例演示了如何在前台实现通知:
public class MyService : Service { …… public override StartCommandResult OnStartCommand(……) { var pendingIntent = PendingIntent.GetActivity(this, 0, new Intent(this, typeof(MainActivity)), PendingIntentFlags.UpdateCurrent); Notification.Builder builder = new Notification.Builder(this) .SetContentTitle("来自MyService的通知") .SetContentText("正在前台运行MyService") .SetContentIntent(pendingIntent) .SetSmallIcon(Resource.Drawable.Icon); Notification notification = builder.Build(); //启动前台任务 StartForeground((int)NotificationFlags.ForegroundService, notification); var t = new Thread(() => { //发送通知 var m = (NotificationManager)GetSystemService(NotificationService); m.Notify(0, notification); //….在此处执行后台运行的任务 …… //停止前台任务 StopForeground(true); …… }); t.Start(); return StartCommandResult.NotSticky; } }
在上面的代码中,先通过Notification对象创建一个通知,然后将此通知传递给StartForeground()方法以及NotificationFlags.ForegroundService标志,这样一来,就可以提醒用户这个前台通知的是正在运行的服务的执行情况。
要移除某个前台任务,只需调用StopForeground()方法即可。可以给该方法传递一个布尔值,指示是否同时移除通知。通过StopForeground()方法停止前台任务的目的是为了减少系统的内存压力,但实际上并没有停止后台服务。
当后台服务真正停止时,如果通知仍在继续,该通知也将同时被移除。
二、Notification的布局方式
Android从5.0开始提供了两个由系统控制的布局区域来显示通知。
当首次发布一个通知时,它会在屏幕左上角显示一个通知图标,如下面左侧的图所示(左上角有三个通知)。当用户下拉或点击通知图标时,它就会把每个通知的详细信息显示出来,如下面右侧的图所示:
Android提供了两个由系统控制的布局区域来显示通知:
- Base layout – 基本布局,这是一种紧凑格式的布局。
- Expanded layout – 可折叠展开的布局。
1、基本布局(Base Layout)
在紧凑格式的基本布局中,一个通知由四部分组成:图标、标题、信息、时间戳:
默认情况下,基本布局区域的高度被限制为64dp。
作为可选项,也可以用大图标或图片代替标准图标。在Android 5.0 及更高版本中,原来的标准图标将会贴在图片的右下角,如下图所示:
从Android 5.0开始,当出现通知时会自动锁屏(如下图所示),用户可以双击通知来解锁设备切换通知的显示和隐藏。在应用程序中,可以设置锁屏界面的可见级别来控制什么时候显示通知,用户也可以选择锁屏时是否允许在通知中显示敏感内容。
除此之外,Android从5.0开始还引入了一个称为上吊式(Heads-up)的高优先级通知格式,这种格式的通知会先从屏幕顶部向下滑落几秒钟,然后再自动退回到原来的通知区域。
Heads-up--“头条悬挂”样式的通知,感觉还不如叫“上吊式”形象呢,反正就是看着好像用橡皮筋吊个人头或者长方体开始时一高一低上下摆动几下的意思,呵呵。
Heads-up主要用于显示一些重要的通知信息。
安卓系统还支持让通知包含元数据,以便对其进行更智能化的排序和显示。利用通知提供的元数据,可以控制如何呈现通知的格式。应用程序可以设置以下类型的元数据:
- Priority – 优先级。确定如何以及何时呈现通知。例如将其设置为high-priority以便用Heads-up的形式显示提醒信息。
- Visibility – 指定锁屏时通知内容是否可见。
- Category – 告诉系统在不同的情况下如何处理通知,比如设备处于“请勿打扰”模式时如何处理通知。
注意:Visibility和Category都是从Android 5.0开始引入的,无法在低版本的系统中使用。
2、扩展布局(Expanded Layouts)
Expanded Layouts是从Android 4.1开始提供的。这种方式允许展开布局的高度,以便可观察更多的内容。例如,下面的图演示了可展开布局默认的通知形式:
当展开该通知时,就会显示全部内容:
Android为单事件的通知提供了3种展开方式:
- Big Text – 不展开时仅显示两行,展开时全部显示。
- Inbox – 不展开时,仅显示新邮件的数量。展开时显示第1封邮件消息或收件箱中的邮件列表。
- Image – 不展开时仅显示消息文本。展开时同时显示文本和图像。
本节后面还会解释如何创建大文本、收件箱和图像的通知。
三、创建Notification
Notification.Builder是从Android 3.0开始引入的类,专门用于创建通知。如果希望应用程序兼容旧版本(<3.0),可以改为用NotificationCompat.Builder类来实现。
Notification.Builder提供了设置通知类型的各种选项和方法。例如:
- 通知内容。包括title、message text、notification icon。
- 通知选用的style。例如Big Text、Inbox、Image。
- 通知的优先级:minimum、low、default、high、maximum。
- 锁屏时通知的可见性:public、private、secret。
- 元数据:帮助Andoid区分和过滤通知。
- 可选的intent,表示触碰通知时启动哪个Activity。
Android提供了一个NotificationManager类负责发布通知。在builder中设置了这些上面的选项后,就可以生成包含设置的通知对象。若要发布通知,将通知对象传递给通知委托,并将它们显示给用户即可。
可以在任何上下文中获取对此类的引用,如某项活动或一项服务。
1、生成通知
下面的步骤说明了如何生成一个通知:
- 实例化Notification.Builder对象。
- 调用Notification.Builder对象提供的方法设置通知选项。
- 调用Notification.Builder对象的Build方法创建一个通知实例。
- 调用NotificationManager的Notify方法发布通知。
每个通知至少要提供下面的信息:
- 一个小图标(24x24 dp)
- 一个比较短的标题(title)
- 通知的文本内容。
下面的代码演示了如何用Notification.Builder生成通知:
// 实例化builder并设置通知元素: Notification.Builder builder = new Notification.Builder (this) .SetContentTitle ("简单通知示例") .SetContentText ("你好,这是第一个通知!") .SetSmallIcon (Resource.Drawable.ic_notification); // 创建通知: Notification notification = builder.Build(); // 获取通知管理器: NotificationManager notificationManager = GetSystemService (Context.NotificationService) as NotificationManager; // 发布通知: const int notificationId = 0; notificationManager.Notify (notificationId, notification);
Notify方法包含2个参数:第1个是int类型的通知标识,第2个是通知的实例。一般情况下,通知标识应该是不重复的,如果重复,那么它将覆盖具有同名标识的通知。
这段代码在Android 5.0及更高版本上的运行效果如下:
时间戳(timestamp)默认是自动设置的,你也可以调用notification builder的SetWhen方法重写该设置。例如:
builder.SetWhen (Java.Lang.JavaSystem.CurrentTimeMillis());
2、允许声音(Sound)和振动(Vibration)
如果希望显示通知时播放声音,可调用builder的SetDefaults方法并传递NotificationDefaults.Sound标志:
Notification.Builder builder = new Notification.Builder (this) .SetContentTitle ("Sample Notification") .SetContentText ("Hello World! This is my first notification!") .SetDefaults (NotificationDefaults.Sound) .SetSmallIcon (Resource.Drawable.ic_notification);
NotificationDefaults.Vibrate用于让设备振动而不是播放声音。如果希望在播放声音时同时也让设备振动,可用下面的办法实现:
builder.SetDefaults (NotificationDefaults.Sound | NotificationDefaults.Vibrate);
默认情况下,Android将用系统默认的通知音。当然也可以指定播放的通知音,例如:
builder.SetSound (RingtoneManager.GetDefaultUri(RingtoneType.Alarm));
或者用默认的通知音播放:
builder.SetSound (RingtoneManager.GetDefaultUri(RingtoneType.Ringtone));
当创建一个通知实例后,也可以直接设置该对象的属性来设置播放音,而不是调用Notification.Builder的SetDefaults方法来实现。例如:
Notification notification = builder.Build();
notification.Defaults |= NotificationDefaults.Vibrate;
设备振动效果只能在真机上测试,在模拟器上看不出来。
3、更新通知
下面的代码演示了如何更新已发布的通知:
// Update the existing notification builder content: builder.SetContentTitle ("Updated Notification"); builder.SetContentText ("Changed to this message."); // Build a notification object with updated content: notification = builder.Build(); // Publish the new notification with the existing ID: notificationManager.Notify (notificationId, notification);
这段代码使用已存在的Notification.Builder对象创建一个新的通知,但是其标识号和原来已存在的通知的标识号相同,此时它会覆盖原来的通知,其效果就是更新了通知。更新后的运行效果如下图:
除非发生了下面的情况之一,否则通知将会一直显示:
- 用户不再让其显示(或者点击了全部清除)。
- 应用程序调用NotificationManager.Cancel方法并传递已发布的通知的标识号。
- 应用程序调用了NotificationManager.CancelAll方法。
4、从通知中启动一个Activity
在Android中,常见的情况是将通知与某个活动相关联(用户点击通知时自动运行该活动),该活动可以驻留在另一个应用程序中或另一项任务中。
要将某个操作(action)添加到通知,可创建PendingIntent对象并将其与通知相关联。PendingIntent是一种特殊的Intent,它允许接收通知的应用程序使用发送应用程序的权限来运行预定义的片段。当用户点击通知时,Android就会启动PendingIntent中指定的Activity。
下面的代码演示了如何将PendingIntent与MainActivity相关联:
// Set up an intent so that tapping the notifications returns to this app: Intent intent = new Intent (this, typeof(MainActivity)); // Create a PendingIntent; we're only using one PendingIntent (ID = 0): const int pendingIntentId = 0; PendingIntent pendingIntent = PendingIntent.GetActivity (this, pendingIntentId, intent, PendingIntentFlags.OneShot); // Instantiate the builder and set notification elements, including pending intent: Notification.Builder builder = new Notification.Builder(this) .SetContentIntent (pendingIntent) .SetContentTitle ("Sample Notification") .SetContentText ("Hello World! This is my first action notification!") .SetSmallIcon (Resource.Drawable.ic_notification); // Build the notification: Notification notification = builder.Build(); // Get the notification manager: NotificationManager notificationManager = GetSystemService (Context.NotificationService) as NotificationManager; // Publish the notification: const int notificationId = 0; notificationManager.Notify (notificationId, notification);
在这段代码中,将PendingIntentFlags.OneShot标志传递给PendingIntent.GetActivity方法,意思是仅使用一次PendingIntent。
运行这段代码将显示下面的通知:
点击通知则回退到原来的活动。
在实际的应用程序中,必须处理用户单击BACK按钮的情况,大多数情况下,退出通知活动将会返回到系统主页(Home Screen)而不是回退到原来的活动。避免回不到原来的活动的处理办法就是通过TaskStackBuilder类为back堆栈创建一个PendingIntent。
另一种实际情况就是可能需要从原来的活动中向通知的活动发送数据。例如,通知可能指出下一个消息已到达,此时显示通知的活动(即屏幕的消息窗口)会获取下一个消息的ID号以便显示它,此时,创建PendingIntent的Activity可以通过PutExtra方法添加Intent需要的数据(比如添加一个字符串)作为要显示的下一个消息传递给原来的活动。
下面的代码演示了如何使用TaskStackBuilder控制back stack,也演示了如何发送一个字符串到通知到SecondActivity:
// Setup an intent for SecondActivity: Intent secondIntent = new Intent (this, typeof(SecondActivity)); // Pass some information to SecondActivity: secondIntent.PutExtra ("message", "Greetings from MainActivity!"); // Create a task stack builder to manage the back stack: TaskStackBuilder stackBuilder = TaskStackBuilder.Create(this); // Add all parents of SecondActivity to the stack: stackBuilder.AddParentStack (Java.Lang.Class.FromType (typeof (SecondActivity))); // Push the intent that starts SecondActivity onto the stack: stackBuilder.AddNextIntent (secondIntent); // Obtain the PendingIntent for launching the task constructed by // stackbuilder. The pending intent can be used only once (one shot): const int pendingIntentId = 0; PendingIntent pendingIntent = stackBuilder.GetPendingIntent (pendingIntentId, PendingIntentFlags.OneShot); // Instantiate the builder and set notification elements, including // the pending intent: Notification.Builder builder = new Notification.Builder (this) .SetContentIntent (pendingIntent) .SetContentTitle ("Sample Notification") .SetContentText ("Hello World! This is my second action notification!") .SetSmallIcon (Resource.Drawable.ic_notification); // Build the notification: Notification notification = builder.Build(); // Get the notification manager: NotificationManager notificationManager = GetSystemService (Context.NotificationService) as NotificationManager; // Publish the notification: const int notificationId = 0; notificationManager.Notify (notificationId, notification);
在这段代码中,应用程序包含了两个活动:MainActivity(包含上面的通知代码),SecondActivity(用户点击通知后看到的活动)。下面是在SecondActivity屏幕上看到的效果:
SecondActivity通过下面的代码获取Intent的PutExtra方法传递的消息:
// Get the message from the intent:
string message = Intent.Extras.GetString ("message", "");
可以看出,它获取到的消息是“Greetings from MainActivity!”。当用户在SecondActivity屏幕上按下Back按钮,导航就会退出该应用程序并回到原来的应用程序。
四、增强Notification基本布局的样式
安卓系统默认的通知为基本布局格式,你也可以调用Notification.Builder方法来增强这种基本的格式。本节演示如何将一个大图片图标添加到通知中,以及如何创建扩大的布局通知的示例。
1、大图标样式
调用builder的SetLargeIcon方法即可显示大图片图标:
builder.SetLargeIcon (BitmapFactory.DecodeResource (Resources, Resource.Drawable.monkey_icon));
下图演示了将小图标改为大图片图标后显示的效果:
注意单击大图片图标时才会在图片的右下角显示小图标。
该图片图标的文件名是Resources/drawable/monkey_icon.png,一般不要将图片图标做的过大,否则显示的通知会非常难看。
2、大文本样式
对于比较长的通知,可以用扩展布局模板的办法来实现(添加BigTextStyle),例如:
// Instantiate the Big Text style: Notification.BigTextStyle textStyle = new Notification.BigTextStyle(); // Fill it with text: string longTextMessage = "I went up one pair of stairs."; longTextMessage += " / Just like me. "; //... textStyle.BigText (longTextMessage); // Set the summary text: textStyle.SetSummaryText ("The summary text goes here."); // Plug this style into the builder: builder.SetStyle (textStyle); // Create the notification and publish it ...
下图是这段代码的显示效果:
大文本(Big Text )通知的最大高度是256 dp。
3、图片样式
图片样式(Image style)也叫大图片样式,利用它可直接在通知中显示一个大图像。注意:图像的最大高度是256 dp,在内存受限的情况下,Android会自动将其缩放到最大高度。
下面的代码演示了如何用BigPictureStyle设置图片样式的通知(Resources/drawable/x_bldg.png文件):
// Instantiate the Image (Big Picture) style: Notification.BigPictureStyle picStyle = new Notification.BigPictureStyle(); // Convert the image to a bitmap before passing it into the style: picStyle.BigPicture (BitmapFactory.DecodeResource (Resources, Resource.Drawable.x_bldg)); // Set the summary text that will appear with the image: picStyle.SetSummaryText ("The summary text goes here."); // Plug this style into the builder: builder.SetStyle (picStyle); // Create the notification and publish it ...
下图是显示的效果:
注意:以紧凑格式(compact format)显示图片通知时调用的是build的SetContentText方法,此时当通知扩展到图像时,仅在图像的上方显示文本摘要。
也可以在通知中显示单独的图像文件,而不是将所有图像全部作为资源打包到.apk文件中。下面的代码演示了如何将SD卡中的图像文件作为通知显示出来:
// Using the Image (Big Picture) style: Notification.BigPictureStyle picStyle = new Notification.BigPictureStyle(); // Read an image from the SD card, subsample to half size: BitmapFactory.Options options = new BitmapFactory.Options(); options.InSampleSize = 2; string imagePath = "/sdcard/Pictures/my-tshirt.jpg"; picStyle.BigPicture (BitmapFactory.DecodeFile (imagePath, options)); // Set the summary text that will appear with the image: picStyle.SetSummaryText ("Check out my new T-Shirt!"); // Plug this style into the builder: builder.SetStyle (picStyle); // Create notification and publish it ...
这段代码加载SD卡上的图像文件(/sdcard/Pictures/my-tshirt.jpg),将其缩放到原始大小的一半后作为通知显示出来。效果如下:
在高级应用中,如果你不知道图像文件的大小,当出现内存溢出异常时,可在捕获的异常中调用BitmapFactory.DecodeFile方法解码该文件。
关于加载大图像的并对其进行解码的详细信息,可参考“Load Large Bitmaps Efficiently”一文的介绍。
4、收件箱样式(Inbox Style)
这种格式将通知文本扩展为中间有一条分割线的两部分分别显示(例如上面的部分以紧缩格式显示收件箱中的邮件摘要,下面的部分显示剩余的邮件条数):
下面的代码演示了这种样式的具体实现办法:
// Instantiate the Inbox style: Notification.InboxStyle inboxStyle = new Notification.InboxStyle(); // Set the title and text of the notification: builder.SetContentTitle ("5 new messages"); builder.SetContentText ("chimchim@xamarin.com"); // Generate a message summary for the body of the notification: inboxStyle.AddLine ("Cheeta: Bananas on sale"); inboxStyle.AddLine ("George: Curious about your blog post"); inboxStyle.AddLine ("Nikko: Need a ride to Evolve?"); inboxStyle.SetSummaryText ("+2 more"); // Plug this style into the builder: builder.SetStyle (inboxStyle);
调用InboxStyle的Addline方法可添加分割线上面显示的主题(body)部分的内容,但是,通知的最大高度仍然被限制为256dp。
当然,Inbox Style不一定显示的都是邮件中收件箱的内容,只要是可分割为两部分分别显示的文本内容都可以采用这种样式。比如把多个通知合并在一起用这种样式显示出来,就可以用这种样式来实现。
五、配置元数据(Configuring Metadata)
Notification.Builder包含了一些设置元数据的方法,例如优先级(priority)、可见性(visibility)、分类(category)等。安卓系统将在“用户偏好设置”中使用此信息,以确定如何以及何时显示通知。
1、Priority
发布某个通知时,确定其优先级设置的两种原则是:
- 在哪出现高优先级的通知。例如优先级高的显示在上方,低的显示在下方。而与通知什么时候发布无关。
- 该通知是否用“Heads-up”的方式显示(即“头条悬挂”样式的通知,Android 5.0及更高版本才能使用此样式),一般只有最高优先级的通知才采用这种方式。
下面的代码演示了如何设置通知的优先级:
builder.SetPriority (NotificationPriority.High);
注意:在Android 6.0中,如果使用“Head-up”样式显示高优先级的通知,必须允许这种通知带声音。
Xamarin.Android为通知的优先级定义了下面的枚举类型的可选项:
- NotificationPriority.Max – 告诉用户有紧急事件(例如传入的呼叫、来自呼叫转移的呼叫、紧急警报等)。
- NotificationPriority.High –重要的事件(如重要的email、实时到达的聊天消息等)。这种通知的消息在Android 5.0及更高版本中也是用“Heads-up”的样式显示。
- NotificationPriority.Default –通知用户有中等程度的重要的事。
- NotificationPriority.Low –不太重要的信息((例如,软件更新提醒、社交网络的更新等)。
- NotificationPriority.Min – 来自后台的通知(例如位置或天气信息)。一般是仅在用户希望查看这些信息时才发出这种通知。
2、Visibility
从Android 5.0开始,这种设置用于控制在锁屏模式下将有多少条通知内容显示出来。Xamarin.Android定义了下面的枚举类型的可选项:
- NotificationVisibility.Public – 在锁屏模式下显示全部的通知内容。
- NotificationVisibility.Private – 这是默认设置。只有基本信息显示在安全锁屏界面(如通知图标和发布的应用程序的名称),但隐藏其余通知的详细信息。
- NotificationVisibility.Secret – 锁屏界面中什么通知都不显示,甚至没有通知图标。只有在用户解锁设备后通知内容才可用。
下面的代码演示了如何设置Visibility:
builder.SetVisibility (NotificationVisibility.Private);
3、Category
从Android 5.0开始,可通过预定义的类别排序和筛选通知。Xamarin.Android提供了以下枚举类型的可选项:
- Notification.CategoryCall – 通知有电话打进来了。
- Notification.CategoryMessage – 通知有文本消息进来了。
- Notification.CategoryAlarm – 通知有警告或实时消息进来了。
- Notification.CategoryEmail – 通知有email消息进来了。
- Notification.CategoryEvent – 日历事件。
- Notification.CategoryPromo – 促销信息或广告。
- Notification.CategoryProgress – 来自后台操作的进度消息。
- Notification.CategorySocial – 公共网络更新。
- Notification.CategoryError – 后台操作或权限操作失败。
- Notification.CategoryTransport – 多媒体播放更新
- Notification.CategorySystem –保留供系统使用 (系统或设备的状态)。
- Notification.CategoryService – 指示一个后台服务正在运行。
- Notification.CategoryRecommendation – 与当前运行的应用程序相关的建议消息。
- Notification.CategoryStatus –有关设备的信息。
当对通知排序时,通知的priority设置优先于category设置。例如,高优先级的通知会用“Heads-up”的方式显示,即使它属于促销类别也是如此。
下面的代码演示了如何设置通知的category属性:
builder.SetCategory (Notification.CategoryCall);
下图演示了如何用基于类别的通知筛选和过滤“请勿打扰”功能(Android 5.0开始提供)的设置:让该功能筛选来电显示通知(即:将“请勿打扰”设置为Enable,不会影响来电通知的显示,但会过滤掉Message通知):
注意:“请勿打扰”模式不影响警告通知的显示,即:Notification.CategoryAlarm通知永远不会被过滤掉。
六、兼容性(Compatibility)
如果你设计的App在低于API Level 4的系统上运行,为了兼容这些旧版本,此时不能用Notification.Builder.,只能用NotificationCompat.Builder类来实现。
由于现在很少用这么低的版本,所以NotificationCompat.Builder的用法就不说了。
下一节我们再用完整例子说明具体用法。