参考:
Android基础入门教程——2.5.2 Notification(状态栏通知)详解
Android Notification 的声音和震动
google API guilde :https://developer.android.google.cn/guide/topics/ui/notifiers/notifications.html
demo:http://git.oschina.net/AndroidUI/CustomNotification01Notification:就是服务器推送的消息,显示在手机最顶部的任务栏的消息。
有关的类和方法
类:
NotificationManager:通知管理器:可以发送和取消通知
Notification:通知对象,可以设置属性
方法:
常用方法
.setContentTitle("标题")
.setContentText("内容内容内容内容内容内容内容")
.setSubText("子内容子内容子内容子内容子内容")
.setTicker("摘要")//通知显示的内容
.setSmallIcon(R.mipmap.ic_launcher)//设置小图标,4.x在右边,5.x在左边
.setLargeIcon(bitmap)//设置大图标
.setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_VIBRATE)//设置默认的呼吸灯和震动
.setContentIntent(pendingIntent)//打开通知后做什么
.setAutoCancel(true);//点击后自动消失
notify(NotificationId, notification);发送通知
3种方式设置灯光+声音+震动
notification.defaults |= Notification.DEFAULT_SOUND;
notification.defaults |= Notification.DEFAULT_VIBRATE;
notification.defaults |= Notification.DEFAULT_LIGHTS;
build.setDefaults(Notification.DEFAULT_ALL)
build.setDefaults(Notification.DEFAULT_SOUND|Notification.DEFAULT_VIBRATE|Notification.DEFAULT_LIGHTS)
关于setVibrate()
vibrate
只可以设置一次,用setVibrate()
就不可以在用Notification.DEFAULT_VIBRATE
,而Notification.DEFAULT_ALL
包含DEFAULT_VIBRATE
,导致无效setVibrate(pattern)
无效
private long[] pattern;
pattern = new long[]{0, 2000, 5000, 2000, 5000, 2000};
builder.setContentTitle("Title")
.setContentText("message" + getResources().getString(R.string.notification_normal))
.setSmallIcon(R.mipmap.ic_launcher_round)
.setTicker("Ticker")
.setAutoCancel(true)
.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_LIGHTS)//和setVibrate(pattern)可以同用
// .setDefaults(Notification.DEFAULT_ALL)//和setVibrate(pattern)不可以同用
.setVibrate(pattern);
flag:
public static final int FLAG_SHOW_LIGHTS = 0x00000001;//控制闪光
public static final int FLAG_ONGOING_EVENT = 0x00000002;//将flag设置为这个属性那么通知就会像QQ一样一直在状态栏显示
public static final int FLAG_INSISTENT = 0x00000004; //重复发出声音,直到用户响应此通知
public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008;//标记声音或者震动一次
public static final int FLAG_AUTO_CANCEL = 0x00000010; //在通知栏上点击此通知后自动清除此通知
public static final int FLAG_NO_CLEAR = 0x00000020;//将flag设置为这个属性那么通知栏的那个清楚按钮就不会出现
public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;//前台服务标记
public static final int FLAG_HIGH_PRIORITY = 0x00000080;
下面通过一个Demo演示通知的使用
https://git.oschina.net/AndroidUI/NotificationDemo.git
MainActivity有2个Button,一个打开通知,一个取消通知。
逻辑代码
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_open = (Button) findViewById(R.id.btn_open);
btn_close = (Button) findViewById(R.id.btn_close);
//打开通知
btn_open.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
initNotification();
}
});
//关闭通知
btn_close.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
manager.cancel(NotificationId);//取消通知(根据id)
// manager.cancelAll();//关闭所有通知
}
});
}
创建通知并设置属性 + 发布通知 + 点击通知跳转到新Activity
//创建通知对象并设置参数
private void initNotification() {
//获取 NotificationManager
manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
//创建pendingIntent对象
setPendingIntent();
//获取bitmap对象
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
builder = new Notification.Builder(this);
builder.setContentTitle("标题")
.setContentText("内容内容内容内容内容内容内容")
.setSubText("子内容子内容子内容子内容子内容")
.setTicker("摘要")//通知显示的内容
.setSmallIcon(R.mipmap.ic_launcher)//设置小图标,4.x在右边,5.x在左边
.setLargeIcon(bitmap)//设置大图标
.setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_VIBRATE | Notification.DEFAULT_SOUND)//设置默认的呼吸灯+震动+声音
.setContentIntent(pendingIntent)//打开通知后做什么
.setAutoCancel(true);//点击后自动消失
notification = builder.build();
notification.flags |= Notification.FLAG_INSISTENT;//不查看通知就一直发出声音和震动
//发布通知
manager.notify(NotificationId, notification);
}
自定义震动时长:
long[] vibrates={0,1000,1000,1000,1000,1000};
notification.vibrate = vibrates;
震动+铃声+闪光:
notification.defaults = Notification.DEFAULT_ALL
获取PendingIntent
private void setPendingIntent() {
Intent intent = new Intent(this, NewActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
pendingIntent = PendingIntent.getActivity(this, 1, intent, 0);
}
Ticker:
ContentTitle + ContentText + SubText + LargeIcon + SmallIcon
自定义通知布局
guilde:https://developer.android.google.cn/guide/topics/ui/notifiers/notifications.html#CustomNotification
sample:https://developer.android.google.cn/samples/CustomNotifications/index.html
效果图
方法
builder方法 | 说明 |
---|---|
setContent(contentView) | 添加自定义布局:高度4行文字 |
setCustomContentView(contentView) | 添加自定义布局:高度4行文字 |
添加自定义布局:高度4行文字 | |
setCustomBigContentView(bigContentView) | 高度最大,全部显示 |
自定义通知布局是通过RemoteViews
实现的,
有2种方法添加自定义布局
第一种:已经过时
notification.contentView = contentView;
notification.bigContentView = bigContentView;
第二种:builder
builder.setContent(contentView)
builder.setCustomContentView(contentView)
builder.setCustomBigContentView(bigContentView)
notification.contentView
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setContentTitle("Title")
.setContentText("message")
.setSmallIcon(R.mipmap.ic_launcher_round)
.setTicker("ticker")
.setAutoCancel(true)
.setSmallIcon(R.mipmap.ic_launcher_round);
Notification notification = builder.build();
//自定义contentView
RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.notification_normal);
contentView.setTextViewText(R.id.tv_normal, getResources().getString(R.string.notification_normal));
notification.contentView = contentView;
//自定义bigContentView
if (Build.VERSION.SDK_INT >= 16) {
RemoteViews bigContentView = new RemoteViews(getPackageName(), R.layout.notification_enpanded);
bigContentView.setTextViewText(R.id.tv_expanded, getResources().getString(R.string.notigication_expanded));
notification.bigContentView = bigContentView;
}
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.notify(0, notification);
builder.setCustomContentView(view)builder.setCustomBigContentView(view)
RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.notification_normal);
contentView.setTextViewText(R.id.tv_normal, getResources().getString(R.string.notification_normal));
RemoteViews bigContentView = new RemoteViews(getPackageName(), R.layout.notification_enpanded);
bigContentView.setTextViewText(R.id.tv_expanded, getResources().getString(R.string.notigication_expanded));
RemoteViews headerContentView = new RemoteViews(getPackageName(), R.layout.notification__header_up);
bigContentView.setTextViewText(R.id.tv_header, "Header");
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setContentTitle("Title")//占用一行
.setContentText("message")//占用一行
.setSmallIcon(R.mipmap.ic_launcher_round)
.setTicker("Ticker")
.setAutoCancel(true)
.setDefaults(Notification.DEFAULT_ALL)
.setContent(contentView)//效果和setCustomContentView一样,同样是占用4行,
.setCustomContentView(contentView)//占用4行
.setCustomBigContentView(bigContentView)//最大
// .setCustomHeadsUpContentView(headerContentView)//无效
;
Notification notification = builder.build();
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.notify(0, notification);
把service设置成前台进程
把service设置成前台进程:
Notification notification = new Notification.Builder(this).build();
startForeground(1, notification);
把service的前台进程销毁:
stopForeground(true);
浮动通知
让通知直接浮动显示,而不是需要下滑通知栏才显示。有2种方法
- 提交Notification
的优先级为Notification.PRIORITY_HIGH
或者Notification.PRIORITY_MAX
,并使用了震动或铃声
- 用户的 Activity 处于全屏模式中(应用使用 fullScreenIntent
)
方式一: 通告优先级
builder.setPriority(Notification.PRIORITY_HIGH)
builder.setPriority(Notification.PRIORITY_MAX)
builder.setDefaults(Notification.DEFAULT_ALL)
方式二:使用fullScreenIntent
Intent intent = new Intent(context, NewActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setFullScreenIntent(pendingIntent, false);//第2个参数:是否是高优先级
Inbox扩展布局
相当于在通知中加入了ListView
,但是没有点击事件
NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
inboxStyle.setBigContentTitle("BigContentTitle:");
for (int i = 0; i < 6; i++) {
inboxStyle.addLine("item " + i);
}
build..setStyle(inboxStyle);
启动Activity时保留导航
就是在MainActivity
中发送Notification
,点击开启NewActivity
,当关闭NewActivity
后,会回到MainActivity
。
根据activity分2种实现方式:
- 常规Activity
- 特殊Activity
那么到底是什么意思呢?我们知道从Notification
打开Activity_A
,必须给Activity_A
设置android:launchMode="singleTask"
或intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
,但是这个Activity_A
只可以在通知种打开,在其他Activity
中无法启动它,我们把Activity_A
成为特殊的Activity
,这个Activity_A
不是工作流的一部分,可以让它既可以从通知中打开Activity_A
,也可以在其他Activity
中打开,这是可以的,这种就是常规Activity
。
常规Activity
第一步:在AndroidManifest.xml中定义NewActivity的层级结构
首先在AndroidManifest.xml中定义NewActivity的层级结构,对于Android4.1只需要设置parentActivityName
即可,值是父Activity
中android:name=".MainActivity"
中name
的值,对于老版本,还需要添加meta-data
。
<activity
android:name=".NewActivity"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity"/>
</activity>
添加android:parentActivityName
属性的目的是:当我们点击通知,打开Activity_A,,当关闭A时,系统会启动该属性对应的Activity(例如MainActivity)。那么问题来了,为什么不直接使用startActivty(…),因为如果当前页就是MainActivity,点击通知打开A,在关闭A,启动MainActivity,加上原来的MainActivity就有2个了,必须finish()2次才可以关闭掉MainActivity。
根据可启动 Activity 的 Intent 创建返回栈:
Intent intent = new Intent(context, NewActivity.class);
TaskStackBuilder builder = TaskStackBuilder.create(context)
.addParentStack(NewActivity.class)
.addNextIntent(intent);
PendingIntent pendingIntent = builder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
build.setContentIntent(pendingIntent)
注意:必须先设置父堆栈,再设置intent,顺序不可错。
TaskStackBuilder builder = TaskStackBuilder.create(context)
.addParentStack(NotificationInfoActivity.class)
.addNextIntent(intent);
特殊Activity
第一步:在清单文件中,将以下属性添加到 Activity 的 元素
<activity
android:name=".NewActivity2"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity=""/>
android:excludeFromRecents="true"
确保点击通知打开NewActivity2
时不会销毁MainActivity
第二步:构建并发出通知
Intent intent = new Intent(context, NewActivity2.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
build.setContentIntent(pendingIntent)
可以在关闭NotificationInfoActivity时启动MAinActivity吗?
不可以,因为如果当前页是MainActivity,此时点击Notification,打开NotificationInfoActivity,关闭NotificationInfoActivity时,我们启动MAinActivity,这样会有2个MAinActivity示例,点击back键一次就之关闭一个MAinActivity,点击2次,才可以全部关闭。
显示进度
分2种:
- 有固定时长的进度显示
- 无固定时长的进度显示
是通过build.setProgress (int max, int progress, boolean indeterminate)
实现的。setProgress(0, 0, false)
清除通知中的progressbar
第一个参数:最大值
第二个参数:当前值
第三个参数:true:不确定时长;false:确定时长
时长不固定
final Notification.Builder builder = new Notification.Builder(context);
builder.setContentTitle("在通知中显示进度一:显示持续 Activity指示器")
.setContentText("message" + getResources().getString(R.string.notification_normal))
.setSmallIcon(R.mipmap.ic_launcher_round)
.setTicker("Ticker")
.setAutoCancel(true)
.setProgress(0, 0, true)
.setDefaults(Notification.DEFAULT_ALL);
final NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i <= 30; i += 5) {
builder.setProgress(0, 0, true);
manager.notify(1, builder.build());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
builder.setContentText("下载完成").setProgress(0, 0, true);//移除通知
manager.notify(7, builder.build());
}
}).start();
时长固定
final Notification.Builder builder = new Notification.Builder(context);
builder.setContentTitle("在通知中显示进度二:显示持续时间固定的进度指示器")
.setContentText("message" + getResources().getString(R.string.notification_normal))
.setSmallIcon(R.mipmap.ic_launcher_round)
.setTicker("Ticker")
.setAutoCancel(true)
.setProgress(100, 0, false)
.setDefaults(Notification.DEFAULT_ALL);
final NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i <= 20; i += 5) {
builder.setProgress(100, i, false);
manager.notify(8, builder.build());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
builder.setContentText("下载完成").setProgress(0, 0, false);//移除通知
manager.notify(8, builder.build());
}
}).start();
锁屏显示
类似于壁纸,API>=21
build.setVisibility(Notification.VISIBILITY_PUBLIC)
您的应用可以控制在安全锁定屏幕上显示的通知中可见的详细级别。 调用 setVisibility()
并指定以下值之一:
VISIBILITY_PUBLIC: 显示通知的完整内容。
VISIBILITY_SECRET: 不会在锁定屏幕上显示此通知的任何部分。
VISIBILITY_PRIVATE: 显示通知图标和内容标题等基本信息,但是隐藏通知的完整内容。
flag:Notification.FLAG_INSISTEN一直提醒
第一次:声音+震动,之后只有声音,不可以与Notification.DEFAULT_ALL共用
build.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE)
notification.flags |= Notification.FLAG_INSISTENT;
demo:
Notification.Builder builder = new Notification.Builder(context);
builder.setContentTitle("一直有声音+震动+呼吸灯")
.setContentText("message" + getResources().getString(R.string.notification_normal))
.setSmallIcon(R.mipmap.ic_launcher_round)
.setTicker("Ticker")
.setAutoCancel(true)
.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE)
.setPriority(Notification.PRIORITY_DEFAULT);
Notification notification = builder.build();
notification.flags |= Notification.FLAG_INSISTENT;
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.notify(13, notification);
bug
1 点击通知,开启的activity是空白页,但是我们设置的activity是有内容的,这是为什么呢?
原因:重写的onCreate(…)方法有2种:第一种:用protected修饰,且只有1个参数:Bundle;第二种:用public修饰,且有2个参数:Bundle + PersistableBundle,第一种是正确的。
有的博客说到:
Android中notification点击进入新activity,会打开多个相同activity,需要在Intent设置如下flag :intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
这种情况我还没遇到。
nm.notify()无效
一定要设置smallIcon(),不然notification不会显示。
setSmallIcon(R.mipmap.logo)