应用程序正在后台运行,当前没有用户可见的界面,但还必须将发生的重要事件通知给用户,就可以使用 NotificationManager 发送状态栏通知。通过所有的系统控件,如 Service,BroadcastReceiver 或 Activity,可以将通知发送到 NotificationManager。关于 Notification 的一些用法可以参见以前的博客的《使用 Notification 在状态栏中显示一个通知。 》http://blog.csdn.net/antimage08/article/details/50067995
下面的示例你会看到一个 Activity ,它在用户离开时发送一系列不同的通知类型,并转入主屏幕。效果如下(另外部分效果见下文):
content_main.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="8dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context=".NotificationActivity"
tools:showIn="@layout/activity_main">
<RadioGroup
android:id="@+id/options_group"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="0dp">
<TextView
android:textAppearance="?android:textAppearanceLarge"
android:text="Rich Styles"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<RadioButton
android:id="@+id/option_basic"
android:text="Basic Notification"
android:checked="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<RadioButton
android:id="@+id/option_bigtext"
android:text="BigText Style"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<RadioButton
android:id="@+id/option_bigpicture"
android:text="BigPicture Style"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<RadioButton
android:id="@+id/option_inbox"
android:text="Inbox Style"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:layout_marginTop="8dp"
android:text="Secured Style"
android:textAppearance="?android:textAppearanceLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<RadioButton
android:id="@+id/option_private"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Public Version Lockscreen" />
<RadioButton
android:id="@+id/option_secret"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Secret Lockscreen"/>
<RadioButton
android:id="@+id/option_headsup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Heads-Up Notification"/>
</RadioGroup>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Post a Notification"
android:onClick="onPostClick"/>
</LinearLayout>
NotificationActivity.java :
package com.crazy.forsystem;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.RadioGroup;
public class NotificationActivity extends AppCompatActivity {
private RadioGroup mOptionsGroup;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mOptionsGroup = (RadioGroup)findViewById(R.id.options_group);
}
public void onPostClick(View v) {
final int noteId = mOptionsGroup.getCheckedRadioButtonId();
final Notification note;
switch (noteId) {
case R.id.option_basic:
case R.id.option_bigtext:
case R.id.option_bigpicture:
case R.id.option_inbox:
note = buildStyledNotification(noteId);
break;
case R.id.option_private:
case R.id.option_secret:
case R.id.option_headsup:
note = buildSecuredNotification(noteId);
break;
default:
throw new IllegalArgumentException("Unknown Type");
}
NotificationManager manager = (NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(noteId, note);
}
private Notification buildStyledNotification(int type) {
Intent launchIntent = new Intent(this, NotificationActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, launchIntent, 0);
// 使用发送的时间创建通知
NotificationCompat.Builder builder =
new NotificationCompat.Builder(NotificationActivity.this);
builder.setSmallIcon(R.drawable.ic_launcher)
.setTicker("发送了什么")
.setWhen(System.currentTimeMillis())
.setAutoCancel(true)
.setDefaults(Notification.DEFAULT_SOUND)
.setContentTitle("完成")
.setContentText("点击这里")
.setContentIntent(contentIntent);
switch (type) {
case R.id.option_basic:
// 返回简单的通知
return builder.build();
case R.id.option_bigtext:
// 包括两个动作(自定义动作)
builder.addAction(android.R.drawable.ic_menu_call, "Call", contentIntent);
builder.addAction(android.R.drawable.ic_menu_recent_history,
"History", contentIntent);
// 在展开时使用 BigTextStyle (一个扩展样式)
NotificationCompat.BigTextStyle textStyle =
new NotificationCompat.BigTextStyle(builder);
textStyle.bigText("Here is some additional text to be displayed when " +
"the notification is "+"in expanded mode. I can fit so much content " +
"into this giant view!");
return textStyle.build();
case R.id.option_bigpicture:
// 添加一个额外的动作
builder.addAction(android.R.drawable.ic_menu_compass,
"View Location", contentIntent);
// 在展开时使用 BigPictureStyle
NotificationCompat.BigPictureStyle pictureStyle =
new NotificationCompat.BigPictureStyle(builder);
pictureStyle.bigPicture(BitmapFactory.decodeResource(
getResources(), R.drawable.dog));
return pictureStyle.build();
case R.id.option_inbox:
// 在展开时使用 InboxStyle
NotificationCompat.InboxStyle inboxStyle =
new NotificationCompat.InboxStyle(builder);
inboxStyle.setSummaryText("4 New Tasks");
inboxStyle.addLine("Male Dinner");
inboxStyle.addLine("Call Mom");
inboxStyle.addLine("Call Wife First");
inboxStyle.addLine("Pick up Kids");
return inboxStyle.build();
default:
throw new IllegalArgumentException("Unknown Type");
}
}
// 这些属性可以由用户的通知设置重写
private Notification buildSecuredNotification(int type) {
Intent launchIntent =
new Intent(this, NotificationActivity.class);
PendingIntent contentIntent =
PendingIntent.getActivity(this, 0, launchIntent, 0 );
// 构造基础通知
NotificationCompat.Builder builder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("账户余额更新")
.setContentText("你的余额为250,请尽快充值!")
.setStyle(new NotificationCompat.BigTextStyle()
.bigText("你的卡上的余额只有250,请尽快支付余下的钱!"))
.setContentIntent(contentIntent);
switch (type) {
case R.id.option_private:
// 为安全的锁定屏幕提供独特的通知版本
Notification publicNote = new Notification.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("存款通知")
.setContentText("你已经收到一条重要消息")
.setContentIntent(contentIntent)
.build();
return builder.setPublicVersion(publicNote).build();
case R.id.option_secret:
// 从安全的锁定屏幕完全隐藏通知
return builder.setVisibility(Notification.VISIBILITY_SECRET).build();
case R.id.option_headsup:
// 在发布时显示警告通知
return builder.setDefaults(Notification.DEFAULT_SOUND)
.setPriority(Notification.PRIORITY_HIGH).build();
default:
throw new IllegalArgumentException("Unknown Type");
}
}
}
在创建通知之前,可以添加其他一些有用的参数,比如当用户在状态栏中展开通知时显示在通知列表中的详细文本信息。
我们向 Builder 传入一个 PendingIntent 参数,它指向我们的 Activity。这个 Intent 使得通知具有可交互功能,即用户在通知列表中点击通知时可以启动一个 Activity。
对于每个通知事件,这个 Intent 都会启动一个新的 Activity。如果更愿意用一个已经存在的 Activity 实例来响应启动(在栈中只有一个),则可以在 AndroidManifest.xml 中进行设置:
android:launchMode="singleTop"
在上面的例子中,在 Nitification.defaults 中加上了系统默认的通知声音,当通知发出时就会播放这个声音。
1、 在上面的例子中,用到了 扩展通知样式:
从 Android4.1 开始,可以在通知视图中直接显示更加丰富的、具有交互性的信息,这就是通知的样式。
Android 默认提供了三种样式(它们都实现了 Notification.Style ):
-----> BigTextStyle:显示更多的文本信息,例如显示一条消息或者公告的全部内容。
-----> BigPictureStyle:显示全彩的大图片。
-----> InboxStyle:提供了一个条目列表,样子有点像 Gmail 等应用程序中的收件箱视图。
其中的 Notification.Style 就是一个接口,应用程序可以实现它来显示任意更加适合自己需求的自定义扩展布局。
(1)、BigTextStyle 样式的通知,它的代码如上。可以通过 Builder 的 addAction() 方法关联自定义动作。这里演示了如何将布局加入整个视图中。本例中,每个动作的响应结果是一样的,但也可以通过为每个动作都关联一个 PendingIntent,这样每个动作就可以在应用程序中有不同的响应结果了。
(2)、BigPictureStyle 样式的通知,除了使用 bigPicture() 方法传递一张要显示的全彩图外,剩下的内容几乎和 BigTextStyle 一样。
(3)、 InboxStyle 样式的通知。在 Notification.InboxStyle 样式下,可以通过 addLine() 方法向列表中添加多个条目。本例使用 setSummaryText() 方法对所有的条目进行了分类,在之前的样式中该方法也是可以使用的。如果程序运行的最低版本目标平台是 Android4.1,可以使用 Nitification.Builder 代替支持库中的 NotificationCompat。本例中,我们可以看到支持库的强大。调用了 API Level 16才有的方法,实际上是支持库在底层进行了版本检验,对于某一平台不支持的方法会忽略;不需要建立判断分支代码来使用新 API。因此,当在 Android4.0 或更早版本的设备上运行这段代码时,只会出现简单的传统通知。
2、通知的可见性和隐私性:
从 Android5.0 开始,无需下拉状态栏就可以使通知在锁定屏幕上完全显示出来,即使设备受到密码保护也是如此。其效果如下(剩余部分的效果):
其中的特性如下三个方面:
(1)、通知的可见性设置控制其在安全锁定屏幕上的默认行为,这意味着对锁定屏幕启动密码(option_secret 按钮)。对于需要简单滑动手势解锁的锁定屏幕,这些功能不会执行任何操作。
系统提供了 3 个通知可见性选项:
-----> VISIBILITY_PRIVATE:通知的编辑版本,仅包含应用程序标题和图标,在设备解锁时可见。这是默认的可见性设置。
-----> VISIBILITY_PUBLIC:无论设置是解锁还是锁定,都会显示完整的通知。
-----> VISIBILITY_SECRET:此通知将在锁定屏幕上完全隐藏。用户必须解锁设备才能看到此通知的存在。
点击相应的按钮后得到的效果如下:
注:本例使用的是第三个选项,但是无论是调整最低的 Android 版本为4.0.3 还是 5.1,或者把跟着把最高版本从 6.0 调整到 5.1 都没有实现 API 文档中所说的效果。
(2)、在 Android4.1 和以后的版本中,通知支持优先级设置(option_headsup 按钮)。在 Android5.0 中,该功能使我们可以在浮动显示模式下显示具有较高优先级的通知(如来电通知)。此模式会将通知与应用程序内容重叠,而不需要等待用户下拉状态栏,从而强迫用户立即回应此通知。优先级设置为 PRIORITY_HIGH 或 PRIORITY_MAX 的通知将在浮动显示模式下尽可能显示出来。效果如下:
(3)、安全通知功能(option_private 按钮),对于此模块,创建了一个基础通知,计划将其作用从包含往来账户余额的用户银行发出的警告。如果用户使设备保持安全,这就是我们应该保护的敏感信息。Android 框架的默认行为将整个通知隐藏在已经编辑视图的后面,如前面的视图。但是此次采取了更友善的方式。第一个用户选项被选中是,我们构造第二个 Notification 实例并将其传递给 setPublicVersion()。这样,在设备解锁时安全地显示此消息。为解锁时则显示的第二个 Notification 中设置的消息。效果如下:
注:虽说在不支持这些 API 的旧平台上,Android 框架将会直接忽略此部分中设置的值,但是经过在 Android5.0 以上的版本中试验(包括部分真机)也没有得到相应的效果。