概述
许多典型的情况下会出现如下要求:要求你通知用户刚才在你应用中发生的事情。有时需要用户做出回应,有时却不需要。例如:
- 当文件存储完成时,一条安全的消息需要查收时 等这类事件。
- 如果应用工作在后台并且这时需要引起用户注意,应用应该创建一个通知允许且方便用户回应。
- 如果应用在执行用户必须等待的任务时(例如加载文件),应用应该显示一个进度条或进度圈。
使用不同的技术可以实现不同的通知任务:
- 一个土司提示(Toast
Notification) ,简短消息。 - 一个状态栏提示(Status
Notification,),持续提醒,并且需要用户的回应。 - 一个对话框提示(Dialog
Notification,),与Activity相关的通知。
这篇文章介绍了这三种提示和实现方式。
Toast Notification
一个 toast 是在屏幕上弹出一条信息,它的大小总是包裹着需要显示的内容,并且当前的 Activity 依然是可见并且可互动的。toast会自动消失,并且不接受任何互动事件。因为 toast 可以在后台的 Service
中创建,所以即使这个应用程序没有显示在屏幕上,仍然可以弹出
toast.
toast 最好用来显示简要的信息,比如断定用户正在注意屏幕时,弹出"File saved". toast 不能接受任何用户互动事件,如果需要用户响应并采取操作,考虑使用 Status Notification 来替代.。
下图显示了Alarm应用的一个toast通知的例子。当一个闹铃开启的时候,一条toast消息显示出来提醒你设置闹铃成功。
基础
首先,用makeText()方法实例化一个Toast对象。该方法需要三个参数:当前应用的Context,文本消息,和toast的持续时间。该方法返回一个实例化过的Toast对象。你可以用show()方法将该toast通知显示出来,见下例:
Context context = getApplicationContext();
CharSequence text = "Hello toast!"
intduration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
这个例子演示大部分你使用toast通知所需要的,你很少会需要其他的。有可能你会想要把toast放置在其他位置或者使用你自己的布局来代替默认的简单文本消息布局。下一节描述了如何使用它们。
你也可以直接调用该方法已避免保留一个Toast对象,例如:
Toast.makeText(context, text, duration).show();
定位你的toast
一个标准toast通知垂直居中出现在靠近屏幕底部的位置。你可以通过setGravity(int,
int, int)方法来改变其位置。三个参数分别是:一个Gravity常量,一个x方向的偏移值和一个y方向的偏移值。
例如,如果你决定让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>
注意LinearLayout元素的ID是“toast_layout"。你必须使用这个ID从XML中展开(inflate)布局layout,如下所示:
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();
首先,通过getLayoutInflater()(或者getSystemService())取得LayoutInflater,然后用inflate(int,
ViewGroup)从XML展开(inflate)布局layout。第一个参数是layout资源ID,第二个参数是根View。你可以使用这个布局来查找其内部更多的View对象。因此获得和定义ImageView和TextView元素的内容。最后,用Toast(Context)创建一个新的toast并设置属性,例如重心和持续时间。然后调用setView(View)并传入布局对象,你就可以通过show()来显示自定义布局的toast了。
注意: 不要使用Toast的公共构造函数,除非你要用setView(View)来定义布局。如果你没有自定义布局,你必须使用makeText(Context,
int, int)来创建toast。
Status Notifications
一个状态通知显示在系统的状态栏上,并且在通知窗口中包含一个icon和通知信息。当用户选择了这个通知,安卓系统便激活一个该通知定义的Intent(通常是启动一个Activity)。你也可以配置这个通知来提醒用户,例如给用户一个声音,震动,和设备上的闪光灯(提示灯)。
当后台服务需要提示用户 需要给出一个事件的答复时 使用状态通知。为了与用户交互,一个后台服务永不自己启动activity。服务应该创建一个状态通知,将启动活动时由用户选定。
基础
一个Activity或Service可以新建一个状态通知。由于Activity只在前台并且处于焦点状态时才会工作,所有状态通知通常是由service创建的。这样,当用户在使用其它应用或设备休眠时,通知可以在后台被创建。创建一个通知,你必须用到两个类:: 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);
把 Notification
传给 NotificationManager
:
private static final int HELLO_ID = 1;
mNotificationManager.notify(HELLO_ID, notification);
此时,用户就可以看到状态栏通知了。
响应通知
状态栏通知的核心部分就是围绕着如何设计用户与用户界面进行交互。你必须在应用程序中提供正确的实现。
有两种常用的状态栏通知,其中之一是日历发出的事件提醒通知,还有一种是收到邮件时邮箱发出的通知。
他们代表两种处理状态栏通知的典型模式:启动一个不属于主程序的 activity,或者启动一整个应用的实例来代表通知的各个状态点.
下面两个段落描述了两种模式下的 activity 返回栈是如何工作的,首先是处理日历的通知:
- 用户正在日历里新建一个事件,它想到需要到邮箱中复制一段信息。
- 用户选择主界面 > 邮箱.
- 在邮箱界面时,日历弹出了一个新会议通知。
- 用户点击通知,将会跳转到日历中的 activity,显示这次会议的具体信息.
- 用户看完了会议信息,按下返回键,将会回到邮箱界面,也是当时收到通知后离开的那个界面.
处理邮件通知:
- 用户正在写一封邮件,此时需要去日历中查看日期.
- 主界面 > 日历.
- 当在日历中时,收到一个新邮件通知.
- 用户选择这个通知,跳转到了这封新邮件的详细信息界面,这个界面代替了之前写邮件的界面,但是之前写的内容会存到草稿中去.
- 用户点击一次返回键,返回到了邮件列表中(邮箱的典型流程),再按一次返回键就会回到日历中去.
在一个类似邮箱通知中,通知启动的界面代表该通知状态中的主程序。举个例子,当点击邮件通知启动邮件界面时,根据收到邮件的数量要么显示邮件列表、要么显示一个邮件详情。为了实现这样的行为,我们将启动新的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默认所有的组件都跑在一个进程当中,所有这样简单启动一个 activity可能会将这个 activity 加入到你的应用的返回栈中。想要正确的实现这样的通知,需要在
manifest 文件中给 activity 添加这些属性: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>
当你从这个 activity 当中启动其他 activity时要十分小心,因为它不是应用一部分,也不会显示在最近的应用中,当有新的数据需要显示时必须要重新启动这个 activity. 最好的办法是从这个activity中启动的其他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();
}
管理通知
NotificationManager
是一个管理所有通知的系统服务。必须通过 getSystemService()
方法来获取它的引用,例如:
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()
通过相应的成员字段,你可以修改每个属性值(除了Context ,通知标题和文本)。当你通过调用setLatestEventInfo()传入新的contentTitle
和contentText 时,应该更新通知文本。然后调用 notify()
来更新通知。(当然,如果你用的是自定义的通知布局,更新这些标题和文本值是没有任何意义的)
设置提示音
使用默认的通知提示音(由用户定义的)提示用户,或者使用一个由你指定的提示音。
使用用户的默认音效,给通知的defaults 属性增加字段 "DEFAULT_SOUND" :
notification.defaults |= Notification.DEFAULT_SOUND;
想要给通知设置自己的声音,将 Uri 附给通知的 sound 属性。下面的代码展示如何使用设备
SD 卡中的音频文件:
notification.sound = Uri.parse("file:///sdcard/notification/ringer.mp3");
接下来的示例展示如何使用 MediaStore
的 ContentProvider
:
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 属性中设置的声音。
设置震动
你可以通过默认的震动提示用户,或者在应用中自定义一种震动形式.
使用默认震动,给通知的 defaults 属性添加 DEFAULT_VIBRATE
值:
notification.defaults |= Notification.DEFAULT_VIBRATE;
要实现自定义的震动,将一个 long 型数组传给 vibrate 属性:
long[] vibrate = {0,100,200,300};
notification.vibrate = vibrate;
长整型数组定义了震动的时长,第一个数是第一次震动需要等待的时间(从发送通知开始计算),第二个数是第一次震动的时长,第三个数是第二次震动的时长,依次类推。这个数组的长度是任意的,但是无法设置循环震动。
注:如果 defaults 属性中包含了 DEFAULT_VIBRATE
值,那么默认的震动形式将会覆盖自定义的震动形式。
设置提示灯闪烁
使用闪光灯来提示用户,可以使用默认的闪光灯,也可以在程序中自定义闪光灯的颜色和形式.
要使用默认的闪光灯设置,需要给 defaults 属性设置添加一个 DEFAULT_LIGHTS
值:
notification.defaults |= Notification.DEFAULT_LIGHTS;
想要实现自己的闪光灯颜色和形式,需要给通知的 ledARGB 属性设置一个值(表示颜色),ledOffMS属性的值表示LED熄灭的时间,ledOnMS属性表示LED亮起的时间,同样需要给 flags 属性添加 FLAG_SHOW_LIGHTS
标志:
notification.ledARGB = 0xff00ff00;
notification.ledOnMS = 300;
notification.ledOffMS = 1000;
notification.flags |= Notification.FLAG_SHOW_LIGHTS;
上面代码示例中,绿色的闪光灯将会亮起0.3秒,熄灭1秒,循环往复。LED并不不能支持所有的颜色,而且不同的设备会支持不同的颜色,所以考虑到兼容所有的硬件,绿色是最常用的通知颜色.
更多功能
还可以使用 Notification
的属性和标志增加更多的功能,一些常用的如下:
FLAG_AUTO_CANCEL
标志- 给通知的 flags 属性增加这个标志后,用户点击通知后,这个通知就会自动取消掉.
FLAG_INSISTENT
flag- 给 flags 属性添加这个标志后,通知音频将会不断重复播放,直到用户对这个通知作出响应.
FLAG_ONGOING_EVENT
flag- 给 flags 属性添加这个标志可以将这个 notification 归类到“正在运行”下。这表示应用仍在运行—也就是说它的进程仍在后台运行,即使界面没有显示(比如音乐或者通话)。
FLAG_NO_CLEAR
flag- 给通知的 flags 属性添加这个标志意味点击"Clear notifications"时不会清除这一个 notification,如果你的通知是正在进行的,通常也要加上这个标志.
number
field- by汉尼拔萝卜:这一个原文中的描述有误,因为版本更新的原因,现在的 number 显示在通知的内容当中,并且数字是任意的。比如我们的短信应用在连续收到信息时,如果用户一直不去查看,那么在通知中将会显示出一共有多少未读短信,这就是这个属性的作用。
iconLevel
field- 这个值表示的是通知使用的
LevelListDrawable
的等级。你可以通过切换你定义在LevelListDrawable中的图片,来让你通知的图标动起来。阅读LevelListDrawable
获取更多信息.
查看 Notification
来去找到更多你可以使用的功能.
创建一个自定义的通知布局
显示在通知窗口的通知通常包含一个标题和消息文本。这是由setLatestEventInfo()方法中的contentTitle 和contentText 参数定义的。然而,你可以使用RemoteViews
.来自定义通知布局。
要自定义通知布局,实例化一个RemoteViews
.对象来加载布局,然后通将这个对象设置给通知的contentView
属性。
下面一个例子有助你更好的理解自定义通知布局:
1.为通知创建一个xml布局文件,如下,该文件名称为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>
Notice 注意这两个TextView 元素所包含的样式属性。在自定义的布局中使用样式属性是非常有必要的,因为通知的背景颜色可能在各种机器上会不一样。从android版本2.3开始,系统为通知使用的文字定义了一个样式。因此,在android版本高于2.3时,你应该使用这个样式来确保通知中的文字不会被背景颜色映衬。
举个例子,在低于2.3版本上要使用标准的文字颜色,应该参照 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>
在高于2.3的版本上要使用系统默认的颜色,参考下面的样式:
<?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>
现在,在android版本高于2.3的设备上,自定义通知中的文字颜色将会和系统默认通知中的文字颜色一样。这非常有用,因为后面的android版本将背景颜色变为深色,沿用系统的样式确保文字的颜色变为了亮色;而且即使背景改变为其他颜色,你的文字也会作出合适的变化。
2.现在,在应用程序代码中,使用RemoteView提供的方法来定义图片和文本。 ,然后将
RemoveViews 对象传给通知的 contentView 属性,像下面这样写:
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;
shown 如代码所示,将包名和布局资源ID传递给了RemoteViews 的构造函数、然后,使用 setImageViewResource()
和setTextViewText()
为ImageView 和TextView定义内容。将你要设置的View
的 ID 和对应的值一起传给这些方法。最后将 RemoteViews 对象传给通知的 contentView 属性。
3.在使用自定义通知时不需要使用setLatestEventInfo()
方法,所以你必须在Notification 的contentIntent 字段中定义Intent,如下所示:
Intent notificationIntent = new Intent(this, MyClass.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.contentIntent = contentIntent;
4. 通知可以像一般通知一样使用:
mNotificationManager.notify(CUSTOM_VIEW_ID, notification);
RemoteViews
类中有一些方法可以方便我们添加 Chronometer
或者是 ProgressBar
到通知的布局中。需要了解更多关于自定义通知布局的信息,请参考 RemoteViews
类.
警告:在自定义通知的布局时,必须要确认你的布局在各种分辨率的设备上都能合适的显示出来。这条建议适合在android中创建的所有布局文件,因为布局资源是非常有限的,因此不要让你的布局文件太复杂,并且要在各种不同的机器上测试.
Dialog Notification
对话框是在当前 activity 上弹出一个小的窗口,对话框下面的 activity 将是去焦点,对话框开始与用户互动。对话框通常显示在正在运行的程序的 notification 和 activity 中。
当你需要显示进度条或者需要用户确认时(比如带有 "OK" 和 "Cancel"按钮),应该弹出对话框。你可以将对话框作为应用 UI 的组成部分,还可以使用它来处理不使用 notification 的情况。各种各样的对话框样式,请见 Dialogs 部分。