学习资料
- Android通知(Notification)的学习
- Android 通知详解
- 百度翻译
- Android各版本查询和开启悬浮窗权限 这个权限和我的需要差那么一点,可是找来找去没找到相应的设置哈。
简介
- 通知是 Android 在您的应用 UI 之外显示的消息,用于向用户提供提醒、来自其他人的通信或来自您的应用的其他及时信息。用户可以点击通知打开您的应用或直接从通知中执行操作
-
通知的分类
实现
- NotificationManager 通知管理器,用来发起、更新、删除通知
- NotificationChannel 通知渠道,8.0及以上配置渠道以及优先级
- NotificationCompat.Builder 通知构造器,用来配置通知的布局显示以及操作相关
普通通知
- 首先添加权限,发送通知需要相关权限,要不然代码就会报错。
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
- 然后构建通知并发送
- 其中小图标要选择合适的图标,大型的图片设置了也不会正常显示。最好设置小的ICON。
- 下面是所有需要的相关参数,其中NotificationManager需要在Activity调用oncreate回调之后初始化,要不然会报错。
- 其他都比较随意,可以按自己想法设置。
private NotificationManager manager;
private NotificationChannel channel;
private NotificationCompat.Builder builder;
private String CHANNEL_ID = "notification_channel_id";
private int NOTIFICATION_ID = 111;
private String channel_name = "消息";
/**
* 普通通知
*/
private void sendNormal() {
channel_name = "普通通知消息";
//设配8.0以上设备
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
//id是一个string自己随意设置,name是消息的名称,第三个参数是消息的重要成都。默认就是普通消息
channel = new NotificationChannel(CHANNEL_ID,channel_name,NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription("这是一个普通通知消息");//消息描述
channel.setShowBadge(false);//是否在桌面显示角标
manager.createNotificationChannel(channel);
}
//点击意图,就是点击了消息后需要跳转的意图
Intent intent = new Intent(this, PropertyAnimationActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_IMMUTABLE);
//构建通知
builder = new NotificationCompat.Builder(this,CHANNEL_ID)
.setContentTitle("普通通知")//标题
.setContentText("这是一条普通通知")//内容
.setSmallIcon(R.mipmap.beauty)//小图标
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.move1))//大图标
.setPriority(NotificationCompat.PRIORITY_DEFAULT)//优先级
.setAutoCancel(true)//自动消失
.setContentIntent(pendingIntent);//跳转配置
manager.notify(NOTIFICATION_ID,builder.build());
}
-
效果图
关键参数
- setContentTitle:通知标题
- setContentText:通知的内容信息
- setSmallIcon:通知左上角的小图标,建议设置小的icon图标
- setLargeIcon:通知右边的大图
-
setPriority:通知的优先级.这里的优先级是通知列表排序的优先级,高优先级的都会在最上面,如图我的重要通知是在中间发送的,但是总是排在最上面一栏。看起来这个还是有延迟的,合并后并没有马上排序,一开始还是按照消息发送时间排序的,重新下拉状态栏才会被排序。
- setAutoCancel:设置通知是否自动消失
- setContentIntent:设置在用户点击通知时的意图,一般设置需要跳转的页面intent
- setDeleteIntent:设置清除通知时的意图。
- addActions:此方法允许您使用文本向通知添加操作,文本是系统通常显示为带有自定义文本图标的按钮的可选图标
重要通知
啊,这个没什么太大的区别,就是把优先级设置为最高级-
额,我发现怎么设置都没用,我去修改了程序的通知权限设置后不论什么类型的消息都是弹窗形式了。
- 代码
private void sendImportant() {
channel_name = "重要通知消息";
channel = null;
manager = null;
manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
//设配8.0以上设备
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//id是一个string自己随意设置,name是消息的名称,第三个参数是消息的重要成都。默认就是普通消息
channel = new NotificationChannel(CHANNEL_ID, channel_name, NotificationManager.IMPORTANCE_HIGH);
channel.setDescription("这是一个重要通知消息");//消息描述
channel.setShowBadge(true);//是否在桌面显示角标
manager.createNotificationChannel(channel);
}
//点击意图,就是点击了消息后需要跳转的意图
Intent intent = new Intent(this, PropertyAnimationActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
Intent intent2 = new Intent(this, FrameAnimationActivity.class);
PendingIntent pendingIntent2 = PendingIntent.getActivity(this, 0, intent2, PendingIntent.FLAG_IMMUTABLE);
//构建通知
builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("重要通知")//标题
.setContentText("这是一条重要通知")//内容
.setSmallIcon(R.mipmap.beauty)//小图标
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.move1))//大图标
.setPriority(NotificationCompat.PRIORITY_MAX)//优先级 这个时通知列表的排序优先级
.setAutoCancel(true)//自动消失
.setContentIntent(pendingIntent)//跳转配置
.setNumber(20)
.addAction(R.mipmap.ic_launcher, "去看看", pendingIntent2)//这里的跳转和setContentIntent的可以不一样,互补影响,而且它的跳转貌似不会消耗的通知。
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
id++;
manager.notify(id, builder.build());
}
-
效果
- 我猜测应该是手机版本问题,更高版本对这些权限的管理越来越严格了。应该不是代码里面就能控制了的,不过对于低版本应该还是能用的。
-
暂时没有找到设置通知悬浮弹出的权限,只能手动去设置里面设置了。而且设置以后所有通知都是这样弹出的。感觉肯定不行,只能再找办法了,肯定不能这样,不重要的通知一直弹悬浮窗用户会恶心的。
通知中添加操作
- 就是设置addAction属性
- 上面截图中的‘去看看’的点击效果就是跳转到设定的页面。
带进度条的通知
- 直接上代码
/**
* 显示进度通知
*/
private void sendProgressNotification(int currentProgress) {
String contentText = "下载中:" + currentProgress +"%";
//加载完成后,两个进度设置为0就好了
if (currentProgress >= 100){
currentProgress = 0;
maxProgress = 0;
contentText = "下载完成!";
}
channel_name = "下载进度消息";
channel = null;
manager = null;
manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
//设配8.0以上设备
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//id是一个string自己随意设置,name是消息的名称,第三个参数是消息的重要成都。默认就是普通消息
channel = new NotificationChannel(CHANNEL_ID, channel_name, NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription("这是一个下载进度消息");//消息描述
channel.setShowBadge(true);//是否在桌面显示角标
manager.createNotificationChannel(channel);
}
//点击意图,就是点击了消息后需要跳转的意图
//构建通知
builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("下载进度")//标题
.setContentText(contentText)//内容
.setSmallIcon(R.mipmap.beauty)//小图标
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.move1))//大图标
.setPriority(NotificationCompat.PRIORITY_MAX)//优先级 这个时通知列表的排序优先级
.setProgress(maxProgress, currentProgress, false);
int id = 10001;//这里id就不能变了,因为更新进度需要是同一个id,要不然就相当于每一个进度就发送了一个通知了
manager.notify(id, builder.build());
}
关键方法
- setProgress: 设置进度的地方,参数分别为最大进度,当前进度和是否确定进度最大值。( false表示确定的进度,比如100,true表示不确定的进度,会一直显示进度动画,直到更新状态完成,或删除通知如何更新进度往下看)
- 注意哈,使用进度通知,id需要唯一哈,因为更新进度需要id来跟新,如果id不一样相当于发了一个新的通知。
- 动图: 进度通知.gif
大文本和大图片通知
/**
* 发送大文本通知
*/
private void sendLargeTextNotification() {
String text = "啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊" +
"啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊" +
"啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊" +
"啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊" +
"啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊" +
"啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊" +
"啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊" +
"啊啊啊啊啊啊啊啊";
channel_name = "大文本消息";
channel = null;
manager = null;
manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
//设配8.0以上设备
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//id是一个string自己随意设置,name是消息的名称,第三个参数是消息的重要成都。默认就是普通消息
channel = new NotificationChannel(CHANNEL_ID, channel_name, NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription("这是一个大文本消息");//消息描述
channel.setShowBadge(true);//是否在桌面显示角标
manager.createNotificationChannel(channel);
}
//点击意图,就是点击了消息后需要跳转的意图
//构建通知
builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("大文本")//标题
.setContentText("")//内容
.setSmallIcon(R.mipmap.beauty)//小图标
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.move1))//大图标
.setPriority(NotificationCompat.PRIORITY_MAX)//优先级 这个时通知列表的排序优先级
.setAutoCancel(true)
.setStyle(new NotificationCompat.BigTextStyle().bigText(text));
id++;//这里id就不能变了,因为更新进度需要是同一个id,要不然就相当于每一个进度就发送了一个通知了
manager.notify(id, builder.build());
}
- 大图片差不多
.setStyle(new NotificationCompat.BigPictureStyle().bigPicture(
BitmapFactory.decodeResource(getResources(),R.drawable.move2)
));
-
效果
不行了,六点了,我要恰饭去了。
- 还是分个上下两集,明天继续搞定下集。
- 贴一下代码吧
package com.southwind.androidfoundation.notification;
import android.app.AppOpsManager;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import com.southwind.androidfoundation.R;
import com.southwind.androidfoundation.animation.FrameAnimationActivity;
import com.southwind.androidfoundation.animation.PropertyAnimationActivity;
import com.southwind.androidfoundation.fourassembly.base.BaseActivity;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.TimerTask;
public class NotificationActivity extends BaseActivity {
private Context context;
private NotificationManager manager;
private NotificationChannel channel;
private NotificationCompat.Builder builder;
private String CHANNEL_ID = "notification_channel_id";
private int NOTIFICATION_ID = 111;
private String channel_name = "消息";
private int id = NOTIFICATION_ID;
private int currentProgress = 30;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
setContentView(R.layout.activity_notification);
super.onCreate(savedInstanceState);
initTAG(this);
context = this;
}
@Override
public void initButton() {
super.initButton();
/**
* 发送普通通知
*/
initButton(R.id.btn_normal, new View.OnClickListener() {
@Override
public void onClick(View v) {
d("发送普通通知");
sendNormal();
}
});
/**
* 发送重要通知
*/
initButton(R.id.btn_import, new View.OnClickListener() {
@Override
public void onClick(View v) {
d("发送重要通知");
if (!checkPermissions(context)) requestPermissions();
else {
sendImportant();
}
}
});
/**
* 发送进度通知
*/
initButton(R.id.btn_progress, new View.OnClickListener() {
@Override
public void onClick(View v) {
d("发送进度通知");
if (!checkPermissions(context)) requestPermissions();
else {
currentProgress += 15;
sendProgressNotification(currentProgress);
}
}
});
/**
* 发送进度通知
*/
initButton(R.id.btn_large_text, new View.OnClickListener() {
@Override
public void onClick(View v) {
d("发送大文本通知");
sendLargeTextNotification();
}
});
}
private void buildNotification() {
channel_name = "普通通知消息";
channel = null;
manager = null;
manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
//设配8.0以上设备
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//id是一个string自己随意设置,name是消息的名称,第三个参数是消息的重要成都。默认就是普通消息
channel = new NotificationChannel(CHANNEL_ID, channel_name, NotificationManager.IMPORTANCE_LOW);
channel.setDescription("这是一个普通通知消息");//消息描述
channel.setShowBadge(true);//是否在桌面显示角标
manager.createNotificationChannel(channel);
}
//点击意图,就是点击了消息后需要跳转的意图
Intent intent = new Intent(this, PropertyAnimationActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
//构建通知
builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("普通通知")//标题
.setContentText("这是一条普通通知")//内容
.setSmallIcon(R.mipmap.beauty)//小图标
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.move1))//大图标
.setPriority(NotificationCompat.PRIORITY_DEFAULT)//优先级
.setAutoCancel(true)//自动消失
.setContentIntent(pendingIntent);//跳转配置
}
/**
* 普通通知
*/
private void sendNormal() {
buildNotification();
id++;
manager.notify(id, builder.build());
}
private void sendImportant() {
channel_name = "重要通知消息";
channel = null;
manager = null;
manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
//设配8.0以上设备
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//id是一个string自己随意设置,name是消息的名称,第三个参数是消息的重要成都。默认就是普通消息
channel = new NotificationChannel(CHANNEL_ID, channel_name, NotificationManager.IMPORTANCE_HIGH);
channel.setDescription("这是一个重要通知消息");//消息描述
channel.setShowBadge(true);//是否在桌面显示角标
manager.createNotificationChannel(channel);
}
//点击意图,就是点击了消息后需要跳转的意图
Intent intent = new Intent(this, PropertyAnimationActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
Intent intent2 = new Intent(this, FrameAnimationActivity.class);
PendingIntent pendingIntent2 = PendingIntent.getActivity(this, 0, intent2, PendingIntent.FLAG_IMMUTABLE);
//构建通知
builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("重要通知")//标题
.setContentText("这是一条重要通知")//内容
.setSmallIcon(R.mipmap.beauty)//小图标
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.move1))//大图标
.setPriority(NotificationCompat.PRIORITY_MAX)//优先级 这个时通知列表的排序优先级
.setAutoCancel(true)//自动消失
.setContentIntent(pendingIntent)//跳转配置
.setNumber(20)
.addAction(R.mipmap.ic_launcher, "去看看", pendingIntent2)//这里的跳转和setContentIntent的可以不一样,互补影响,而且它的跳转貌似不会消耗的通知。
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
id++;
manager.notify(id, builder.build());
}
private int maxProgress = 100;
/**
* 显示进度通知
*/
private void sendProgressNotification(int currentProgress) {
String contentText = "下载中:" + currentProgress + "%";
//加载完成后,两个进度设置为0就好了
if (currentProgress >= 100) {
currentProgress = 0;
maxProgress = 0;
contentText = "下载完成!";
}
channel_name = "下载进度消息";
channel = null;
manager = null;
manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
//设配8.0以上设备
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//id是一个string自己随意设置,name是消息的名称,第三个参数是消息的重要成都。默认就是普通消息
channel = new NotificationChannel(CHANNEL_ID, channel_name, NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription("这是一个下载进度消息");//消息描述
channel.setShowBadge(true);//是否在桌面显示角标
manager.createNotificationChannel(channel);
}
//点击意图,就是点击了消息后需要跳转的意图
//构建通知
builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("下载进度")//标题
.setContentText(contentText)//内容
.setSmallIcon(R.mipmap.beauty)//小图标
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.move1))//大图标
.setPriority(NotificationCompat.PRIORITY_MAX)//优先级 这个时通知列表的排序优先级
.setProgress(maxProgress, currentProgress, false);
int id = 10001;//这里id就不能变了,因为更新进度需要是同一个id,要不然就相当于每一个进度就发送了一个通知了
manager.notify(id, builder.build());
}
/**
* 发送大文本通知
*/
private void sendLargeTextNotification() {
String text = "啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊" +
"啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊" +
"啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊" +
"啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊" +
"啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊" +
"啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊" +
"啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊" +
"啊啊啊啊啊啊啊啊";
channel_name = "大文本消息";
channel = null;
manager = null;
manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
//设配8.0以上设备
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//id是一个string自己随意设置,name是消息的名称,第三个参数是消息的重要成都。默认就是普通消息
channel = new NotificationChannel(CHANNEL_ID, channel_name, NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription("这是一个大文本消息");//消息描述
channel.setShowBadge(true);//是否在桌面显示角标
manager.createNotificationChannel(channel);
}
//点击意图,就是点击了消息后需要跳转的意图
//构建通知
builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("大文本")//标题
.setContentText("")//内容
.setSmallIcon(R.mipmap.beauty)//小图标
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.move1))//大图标
.setPriority(NotificationCompat.PRIORITY_MAX)//优先级 这个时通知列表的排序优先级
.setAutoCancel(true)
// .setStyle(new NotificationCompat.BigTextStyle().bigText(text))
.setStyle(new NotificationCompat.BigPictureStyle().bigPicture(
BitmapFactory.decodeResource(getResources(),R.drawable.move2)
));
id++;//这里id就不能变了,因为更新进度需要是同一个id,要不然就相当于每一个进度就发送了一个通知了
manager.notify(id, builder.build());
}
/**
* 检查权限
*/
private boolean checkPermissions(Context context) {
String packageName = context.getPackageName();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
return true;
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
try {
Class cls = Class.forName("android.content.Context");
Field f = cls.getDeclaredField("APP_OPS_SERVICE");
f.setAccessible(true);
Object o = f.get(cls);
if (!(o instanceof String)) {
return false;
}
String str = (String) o;
o = cls.getMethod("getSystemService", Class.class).invoke(context, str);
cls = Class.forName("android.app.AppOpsManager");
Field f2 = cls.getDeclaredField("MODE_ALLOWED");
f2.setAccessible(true);
Method m = cls.getMethod("checkOp", String.class, int.class, String.class);
int res = (int) m.invoke(o, 24, Binder.getCallingPid(), context.getPackageName());
return res == f2.getInt(cls);
} catch (Exception e) {
e.printStackTrace();
}
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
if (appOpsManager == null) return false;
PackageManager pm = context.getPackageManager();
try {
ApplicationInfo info = pm.getApplicationInfo(packageName, PackageManager.GET_ACTIVITIES);
int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW, info.uid, packageName);
return mode == AppOpsManager.MODE_ALLOWED || mode == AppOpsManager.MODE_IGNORED;
} catch (Exception e) {
e.printStackTrace();
}
} else
return Settings.canDrawOverlays(context);
}
return false;
}
private void requestPermissions() {
int sdkInt = Build.VERSION.SDK_INT;
if (sdkInt >= Build.VERSION_CODES.O)//8.0以上
{
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
startActivityForResult(intent, 10001);
} else if (sdkInt >= Build.VERSION_CODES.M)//6.0-8.0
{
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 10001);
} else {//4.4-6.0
//无需处理
}
}
}