概述
- 当文件存储完成时,一条安全的消息需要查收时 等这类事件。
- 如果应用工作在后台并且这时需要引起用户注意,应用应该创建一个通知允许且方便用户回应。
- 如果应用在执行用户必须等待的任务时(例如加载文件),应用应该显示一个进度条或进度圈。
- 一个土司提示(Toast Notification) ,简短消息。
- 一个状态栏提示(Status Notification,),持续提醒,并且需要用户的回应。
- 一个对话框提示(Dialog Notification,),与Activity相关的通知。
Toast Notification
一个 toast 是在屏幕上弹出一条信息,它的大小总是包裹着需要显示的内容,并且当前的 Activity 依然是可见并且可互动的。toast会自动消失,并且不接受任何互动事件。因为 toast 可以在后台的 Service
中创建,所以即使这个应用程序没有显示在屏幕上,仍然可以弹出 toast.
toast 最好用来显示简要的信息,比如断定用户正在注意屏幕时,弹出"File saved". toast 不能接受任何用户互动事件,如果需要用户响应并采取操作,考虑使用 Status Notification 来替代.。
下图显示了Alarm应用的一个toast通知的例子。当一个闹铃开启的时候,一条toast消息显示出来提醒你设置闹铃成功。
基础
Context context = getApplicationContext();
CharSequence text = "Hello toast!"
intduration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
你也可以直接调用该方法已避免保留一个Toast对象,例如:
Toast.makeText(context, text, duration).show();
定位你的toast
toast.setGravity(Gravity.TOP | Gravity.LEFT, 0, 0);
自定义toast视图
如果一个简单的文本消息已经无法满足你的需求,你可以自己定义一个toast通知的布局layout。在XML或者代码中定义一个View的布局,并根View对象传递给setView(View)方法。
例如,你可以用如下所示XML创建右图中的toast通知的布局(保存为toast_layout.xml)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/toast_layout_root"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dp"
android:background="#DAAA" >
<ImageView android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginRight="10dp"/>
<TextView android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_textColor="#FFF"/>
</LinearLayout>
LayoutInflater inflater = getLayoutInflater();
View layout = inflater.inflate(R.layout.toast_layout,
(ViewGroup) findViewById(R.id.toast_layout_root));
ImageView image = (ImageView) layout.findViewById(R.id.image);
image.setImageResource(R.drawable.android);
TextView text = (TextView) layout.findViewById(R.id.text);
text.setText("Hello! This is a custom toast!");
Toast toast = new Toast(getApplicationContext());
toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
toast.setDuration(Toast.LENGTH_LONG);
toast.setView(layout);
toast.show();
Status Notifications
基础
Notification
和
NotificationManager
.
Notification
的一个实例定义你状态通知的属性,如状态图标,通知消息,和扩展设置(播放提示音等)。
NotificationManager
是安卓系统服务,它执行和管理所有的状态通知。 你不要直接实例化
NotificationManager
。
应该使用
getSystemService()
方法来获取
NotificationManager
对象的引用。然后,通过
notify()
方法将你的
Notification
发送出去。
NotificationManager的引用:
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
Notification
:
int icon = R.drawable.notification_icon;
CharSequence tickerText = "Hello";
long when = System.currentTimeMillis();
Notification notification = new Notification(icon, tickerText, when);
PendingIntent
:
Context context = getApplicationContext();
CharSequence contentTitle = "My notification";
CharSequence contentText = "Hello World!";
Intent notificationIntent = new Intent(this, MyClass.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
private static final int HELLO_ID = 1;
mNotificationManager.notify(HELLO_ID, notification);
响应通知
- 用户正在日历里新建一个事件,它想到需要到邮箱中复制一段信息。
- 用户选择主界面 > 邮箱.
- 在邮箱界面时,日历弹出了一个新会议通知。
- 用户点击通知,将会跳转到日历中的 activity,显示这次会议的具体信息.
- 用户看完了会议信息,按下返回键,将会回到邮箱界面,也是当时收到通知后离开的那个界面.
- 用户正在写一封邮件,此时需要去日历中查看日期.
- 主界面 > 日历.
- 当在日历中时,收到一个新邮件通知.
- 用户选择这个通知,跳转到了这封新邮件的详细信息界面,这个界面代替了之前写邮件的界面,但是之前写的内容会存到草稿中去.
- 用户点击一次返回键,返回到了邮件列表中(邮箱的典型流程),再按一次返回键就会回到日历中去.
makeMessageIntentStack()
,它构造了代表新的acticity栈中的 intents.(如果使用fragments,你会需要初始化你的 fragment和应用状态,以便当用户点击
返回
时,会返回到fragment的上一个状态) 这个方法的核心代码是
Intent.makeRestartActivityTask()
方法,它会使用合适的标志创建 activity 栈中的根 activity,比如
Intent.FLAG_ACTIVITY_CLEAR_TASK
.
/**
* 这个方法创建了一个Intent 对象数组,这些intent 代表了activity 栈中传入的消息细节,当从通知中启动时会被使用。
*/
static Intent[] makeMessageIntentStack(Context context, CharSequence from,
CharSequence msg) {
// 用户深入应用程序中时,一个代表数据的典型通知发布了出来
// 为了实现这个,需要创建一个intents 数组来表示显示的层级顺序。
.
Intent[] intents = new Intent[4];
// First:该实例的跟activity
// 这是一个让Intent 启动或重置应用栈的简便方法
intents[0] = Intent.makeRestartActivityTask(new ComponentName(context,
com.example.android.apis.ApiDemos.class));
// "App"
intents[1] = new Intent(context, com.example.android.apis.ApiDemos.class);
intents[1].putExtra("com.example.android.apis.Path", "App");
// "App/Notification"
intents[2] = new Intent(context, com.example.android.apis.ApiDemos.class);
intents[2].putExtra("com.example.android.apis.Path", "App/Notification");
// 现在,这个activity展示在了用户面前。当然它的所有数据也一并展示。
intents[3] = new Intent(context, IncomingMessageView.class);
intents[3].putExtra(IncomingMessageView.KEY_FROM, from);
intents[3].putExtra(IncomingMessageView.KEY_MESSAGE, msg);
return intents;
}
/**
* 状态栏通知相关的图标和扩展条目
*/
void showAppNotification() {
// 查找 notification manager service
NotificationManager nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
// 模拟数据的细节
CharSequence from = "Joe";
CharSequence message;
switch ((new Random().nextInt()) % 3) {
case 0: message = "r u hungry? i am starved"; break;
case 1: message = "im nearby u"; break;
default: message = "kthx. meet u for dinner. cul8r"; break;
}
// 当用户点击这个通知时,
PendingIntent 会启动我们的activity// 注意
FLAG_CANCEL_CURRENT// 如果已经有一个匹配的intent 在等待,取消并替换它为数组中最新的intent
PendingIntent contentIntent = PendingIntent.getActivities(this, 0,
makeMessageIntentStack(this, from, message), PendingIntent.FLAG_CANCEL_CURRENT);
// The ticker text, this uses a formatted string so our message could be localized
String tickerText = getString(R.string.imcoming_message_ticker_text, message);
// 构造Notification object.
Notification notif = new Notification(R.drawable.stat_sample, tickerText,
System.currentTimeMillis());
// 在通知面板显示的信息
notif.setLatestEventInfo(this, from, message, contentIntent);
// 我们要这个通知发出提示音,震动和闪烁led灯
// 注:如果你要使用这些功能,你应该有一个选项面板以便于用户可以根据自己喜好开启或关闭这些功能,
notif.defaults = Notification.DEFAULT_ALL;
//注:我们使用的是R.layout.incoming_message_panel 作为我们通知的id
// 它可以是你想的任何整数
// 但是为了方便,使用了与字符有个的资源id
// 它在你的应用中永远会是独一的数字。
nm.notify(R.string.imcoming_message_ticker_text, notif);
}
在一个日历的通知中,通知启动的界面通常不属于应用程序的进程。比如当用户收到日历的通知时,选择这个通知将会启动一个特殊界面来显示代办事项—这个界面仅能从通知中启动,无法通过日历导航过去.
下面的代码发送了一条这样的通知,与上面的通知相似,但是 PendingIntent
只能展示一个activity,也就是专门用来显示代办事项的activity.
/**
* 这个通知是状态栏通知
*/
void showInterstitialNotification() {
//查找 the notification manager service
NotificationManager nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
// The details of our fake message
CharSequence from = "Dianne";
CharSequence message;
switch ((new Random().nextInt()) % 3) {
case 0: message = "i am ready for some dinner"; break;
case 1: message = "how about thai down the block?"; break;
default: message = "meet u soon. dont b late!"; break;
}
// The PendingIntent to launch our activity if the user selects this
// notification. Note the use of FLAG_CANCEL_CURRENT so that, if there
// is already an active matching pending intent, cancel it and replace
// it with the new Intent.
Intent intent = new Intent(this, IncomingMessageInterstitial.class);
intent.putExtra(IncomingMessageView.KEY_FROM, from);
intent.putExtra(IncomingMessageView.KEY_MESSAGE, message);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
intent, PendingIntent.FLAG_CANCEL_CURRENT);
// The ticker text, this uses a formatted string so our message could be localized
String tickerText = getString(R.string.imcoming_message_ticker_text, message);
// construct the Notification object.
Notification notif = new Notification(R.drawable.stat_sample, tickerText,
System.currentTimeMillis());
// Set the info for the views that show in the notification panel.
notif.setLatestEventInfo(this, from, message, contentIntent);
// We'll have this notification do the default sound, vibration, and led.
// Note that if you want any of these behaviors, you should always have
// a preference for the user to turn them off.
notif.defaults = Notification.DEFAULT_ALL;
// Note that we use R.layout.incoming_message_panel as the ID for
// the notification. It could be any integer you want, but we use
// the convention of using a resource id for a string related to
// the notification. It will always be a unique number within your
// application.
nm.notify(R.string.imcoming_message_ticker_text, notif);
}
android:launchMode="singleTask"
,
android:taskAffinity=""
和
android:excludeFromRecents="true"
.完整的描述如下:
<activity android:name=".app.IncomingMessageInterstitial"
android:label="You have messages"
android:theme="@style/ThemeHoloDialog"
android:launchMode="singleTask"
android:taskAffinity=""
android:excludeFromRecents="true">
</activity>
makeMessageIntentStack()
方法,完成类似下面的点击事件:
/**
* 切换app 一个新的activity 栈已经开启代替目前运行的activity之后,该activity 结束。
*/
void switchToApp() {
//展示给给用户选择启动哪个app
//
CharSequence from = getIntent().getCharSequenceExtra(IncomingMessageView.KEY_FROM);
CharSequence msg = getIntent().getCharSequenceExtra(IncomingMessageView.KEY_MESSAGE);
// Build the new activity stack, launch it, and finish this UI.
Intent[] stack = IncomingMessage.makeMessageIntentStack(this, from, msg);
startActivities(stack);
finish();
}
管理通知
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
notify(int, Notification)
.方法将
Notification
传递给
NotificationManager。
first
第一个参数是通知的唯一ID,第二个参数是
Notification
对象。ID是你应用中通知的唯一标识。ID是必要的,如果你需要更新通知或者(如果你应用中组织了各种各样的通知)
当用户通过定义在通知中的 intent 返回你的应用时,需要选择合适的 action.
Notification
.
添加
"FLAG_AUTO_CANCEL"flag 。也可以通过
cancel(int)
手动清除,传递给它通知的ID,或者清除所有的通知
cancelAll()
.
创建通知
- 它的图标
- 它的标题和消息内容,除非用的是你自定义的布局。
- 一个
PendingIntent
, 当通知被选中时激活。
- 一个显示在状态栏的提示信息
- 提示音
- 震动
- 闪烁LED灯
Notification(int, CharSequence, long)
构造器和
setLatestEventInfo(Context, CharSequence, CharSequence, PendingIntent)
方法。他们定义了一个通知的所需的配置。下面的片段创建了一个基本通知:
int icon = R.drawable.notification_icon; // icon from resources
CharSequence tickerText = "Hello"; // ticker-text
long when = System.currentTimeMillis(); // notification time
Context context = getApplicationContext(); // application Context
CharSequence contentTitle = "My notification"; // message title
CharSequence contentText = "Hello World!"; // message text
Intent notificationIntent = new Intent(this, MyClass.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
// the next two lines initialize the Notification, using the configurations above
Notification notification = new Notification(icon, tickerText, when);
notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
更新通知
NotificationManager
管理的整数ID,你可以通过调用
setLatestEventInfo()
附加新的值来修改你的通知,改变一些通知的字段值,然后再次调用
notify()
notify()
来更新通知。(当然,如果你用的是自定义的通知布局,更新这些标题和文本值是没有任何意义的)
设置提示音
notification.defaults |= Notification.DEFAULT_SOUND;
notification.sound = Uri.parse("file:///sdcard/notification/ringer.mp3");
notification.sound = Uri.withAppendedPath(Audio.Media.INTERNAL_CONTENT_URI, "6");
这个示例中,音频文件的ID是已知的并且被附到 Uri
后面。如果不知到确切的 ID值,必须通过 ContentResolver
去查询所有存储在 MediaStore
中的音频文件。怎样使用 ContentResolver
可以在 Content Providers 中查看。
如果你需要一直循环播放声音直到用户点击通知或者取消通知,给通知的 flags 属性添加 FLAG_INSISTENT
值。
注:如果 defaults 属性中有 DEFAULT_SOUND
这个值,那么默认的声音将会覆盖 sound 属性中设置的声音。
设置震动
notification.defaults |= Notification.DEFAULT_VIBRATE;
long[] vibrate = {0,100,200,300};
notification.vibrate = vibrate;
设置提示灯闪烁
使用闪光灯来提示用户,可以使用默认的闪光灯,也可以在程序中自定义闪光灯的颜色和形式.
要使用默认的闪光灯设置,需要给 defaults 属性设置添加一个 DEFAULT_LIGHTS
值:
notification.defaults |= Notification.DEFAULT_LIGHTS;
FLAG_SHOW_LIGHTS
标志:
notification.ledARGB = 0xff00ff00;
notification.ledOnMS = 300;
notification.ledOffMS = 1000;
notification.flags |= Notification.FLAG_SHOW_LIGHTS;
更多功能
还可以使用 Notification
的属性和标志增加更多的功能,一些常用的如下:
- 给通知的 flags 属性增加这个标志后,用户点击通知后,这个通知就会自动取消掉.
- 给 flags 属性添加这个标志后,通知音频将会不断重复播放,直到用户对这个通知作出响应.
- 给 flags 属性添加这个标志可以将这个 notification 归类到“正在运行”下。这表示应用仍在运行—也就是说它的进程仍在后台运行,即使界面没有显示(比如音乐或者通话)。
- 给通知的 flags 属性添加这个标志意味点击"Clear notifications"时 不会清除这一个 notification,如果你的通知是正在进行的,通常也要加上这个标志.
- by汉尼拔萝卜:这一个原文中的描述有误,因为版本更新的原因,现在的 number 显示在通知的内容当中,并且数字是任意的。比如我们的短信应用在连续收到信息时,如果用户一直不去查看,那么在通知中将会显示出一共有多少未读短信,这就是这个属性的作用。
-
这个值表示的是通知使用的
LevelListDrawable
的等级。你可以通过切换你定义在LevelListDrawable中的图片,来让你通知的图标动起来。阅读LevelListDrawable
获取更多信息.
FLAG_AUTO_CANCEL
标志
FLAG_INSISTENT
flag
FLAG_ONGOING_EVENT
flag
FLAG_NO_CLEAR
flag
number
field
iconLevel
field
查看 Notification
来去找到更多你可以使用的功能.
创建一个自定义的通知布局
RemoteViews
.来自定义通知布局。
custom_notification.xml
:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dp" >
<ImageView android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_alignParentLeft="true"
android:layout_marginRight="10dp" />
<TextView android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/image"
style="@style/NotificationTitle" />
<TextView android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/image"
android:layout_below="@id/title"
style="@style/NotificationText" />
</RelativeLayout>
res/values/styles.xml
中的样式:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="NotificationText">
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="NotificationTitle">
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:textStyle">bold</item>
</style>
<!-- If you want a slightly different color for some text,
consider using ?android:attr/textColorSecondary -->
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="NotificationText" parent="android:TextAppearance.StatusBar.EventContent" />
<style name="NotificationTitle" parent="android:TextAppearance.StatusBar.EventContent.Title" />
</resources>
RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.custom_notification_layout);
contentView.setImageViewResource(R.id.image, R.drawable.notification_image);
contentView.setTextViewText(R.id.title, "Custom notification");
contentView.setTextViewText(R.id.text, "This is a custom layout");
notification.contentView = contentView;
setImageViewResource()
和
setTextViewText()
为
ImageView
和
TextView定义内容。
将你要设置的View 的 ID 和对应的值一起传给这些方法。最后将 RemoteViews 对象传给通知的
contentView
属性。
Intent notificationIntent = new Intent(this, MyClass.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.contentIntent = contentIntent;
mNotificationManager.notify(CUSTOM_VIEW_ID, notification);
RemoteViews
类中有一些方法可以方便我们添加
Chronometer
或者是
ProgressBar
到通知的布局中。需要了解更多关于自定义通知布局的信息,请参考
RemoteViews
类.
Dialog Notification
对话框是在当前 activity 上弹出一个小的窗口,对话框下面的 activity 将是去焦点,对话框开始与用户互动。对话框通常显示在正在运行的程序的 notification 和 activity 中。
当你需要显示进度条或者需要用户确认时(比如带有 "OK" 和 "Cancel"按钮),应该弹出对话框。你可以将对话框作为应用 UI 的组成部分,还可以使用它来处理不使用 notification 的情况。各种各样的对话框样式,请见 Dialogs 部分。