8.0系统的通知栏适配:
从Android 8.0系统开始,Google引入了通知渠道这个概念。
每个App都可以自由地创建当前App拥有哪些通知渠道,但是这些通知渠道的控制权都是掌握在用户手上的。用户可以自由地选择这些通知渠道的重要程度,是否响铃、是否振动、或者是否要关闭这个渠道的通知。
如果你将项目中的targetSdkVersion指定到了26或者更高,那么Android系统就会认为你的App已经做好了8.0系统的适配工作,当然包括了通知栏的适配。这个时候如果还不使用通知渠道的话,那么你的App的通知将完全无法弹出。
创建通知渠道:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
String channelId = "chat";
String channelName = "聊天消息";
int importance = NotificationManager.IMPORTANCE_HIGH;
createNotificationChannel(channelId, channelName, importance);
channelId = "subscribe";
channelName = "订阅消息";
importance = NotificationManager.IMPORTANCE_DEFAULT;
createNotificationChannel(channelId, channelName, importance);
}
}
@TargetApi(Build.VERSION_CODES.O)
private void createNotificationChannel(String channelId, String channelName, int importance) {
NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
NotificationManager notificationManager = (NotificationManager) getSystemService(
NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
}
}
创建一个通知渠道的方式非常简单,这里我封装了一个createNotificationChannel()方法,里面的逻辑相信大家都看得懂。需要注意的是,创建一个通知渠道至少需要渠道ID、渠道名称以及重要等级这三个参数,其中渠道ID可以随便定义,只要保证全局唯一性就可以。渠道名称是给用户看的,需要能够表达清楚这个渠道的用途。重要等级的不同则会决定通知的不同行为,当然这里只是初始状态下的重要等级,用户可以随时手动更改某个渠道的重要等级,App是无法干预的。
现在就可以运行一下代码了,运行成功之后我们关闭App,进入到设置 -> 应用 -> 通知当中,查看NotificationTest这个App的通知界面,如下图所示:
让通知显示出来:
public class MainActivity extends AppCompatActivity {
...
public void sendChatMsg(View view) {
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Notification notification = new NotificationCompat.Builder(this, "chat")
.setContentTitle("收到一条聊天消息")
.setContentText("今天中午吃什么?")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.drawable.icon)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.icon))
.setAutoCancel(true)
.build();
manager.notify(1, notification);
}
public void sendSubscribeMsg(View view) {
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Notification notification = new NotificationCompat.Builder(this, "subscribe")
.setContentTitle("收到一条订阅消息")
.setContentText("地铁沿线30万商铺抢购中!")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.drawable.icon)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.icon))
.setAutoCancel(true)
.build();
manager.notify(2, notification);
}
}
由于这是一条重要等级高的通知,因此会使用这种屏幕弹窗的方式来通知用户有消息到来。然后我们可以下拉展开通知栏,这里也能查看到通知的详细信息:
管理通知渠道:
public class MainActivity extends AppCompatActivity {
...
public void sendChatMsg(View view) {
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = manager.getNotificationChannel("chat");
if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
intent.putExtra(Settings.EXTRA_CHANNEL_ID, channel.getId());
startActivity(intent);
Toast.makeText(this, "请手动将通知打开", Toast.LENGTH_SHORT).show();
}
}
Notification notification = new NotificationCompat.Builder(this, "chat")
...
.build();
manager.notify(1, notification);
}
...
}
这里我们对sendChatMsg()方法进行了修改,通过getNotificationChannel()方法获取到了NotificationChannel对象,然后就可以读取该通知渠道下的所有配置了。这里我们判断如果通知渠道的importance等于IMPORTANCE_NONE,就说明用户将该渠道的通知给关闭了,这时会跳转到通知的设置界面提醒用户手动打开。
删除渠道:
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.deleteNotificationChannel(channelId);
显示未读角标:
public class MainActivity extends AppCompatActivity {
...
@TargetApi(Build.VERSION_CODES.O)
private void createNotificationChannel(String channelId, String channelName, int importance) {
NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
channel.setShowBadge(true);
NotificationManager notificationManager = (NotificationManager) getSystemService(
NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
}
public void sendSubscribeMsg(View view) {
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Notification notification = new NotificationCompat.Builder(this, "subscribe")
...
.setNumber(2)
.build();
manager.notify(2, notification);
}
}
可以看到,这里我们主要修改了两个地方。第一是在创建通知渠道的时候,调用了NotificationChannel的setShowBadge(true)方法,表示允许这个渠道下的通知显示角标。第二是在创建通知的时候,调用了setNumber()方法,并传入未读消息的数量。
现在重新运行一下程序,并点击发送订阅消息按钮,然后在Launcher中找到NotificationTest这个应用程序,如下图所示:
可以看到,在图标的右上角有个绿色的角标,说明我们编写的角标功能已经生效了。
需要注意的是,即使我们不调用setShowBadge(true)方法,Android系统默认也是会显示角标的,但是如果你想禁用角标功能,那么记得一定要调用setShowBadge(false)方法。
但是未读数量怎么没有显示出来呢?这个功能还需要我们对着图标进行长按才行,效果如下图所示:
通知栏进度条:
通知栏进度条工具类NitifierManager:
public class NitifierManager {
private static NitifierManager instance;
private Notification.Builder builder;
private static Context mContext;
private static NotificationManager nm;
private static Resources res;
private static final int DOWN_LOAD_FILE = 7; // 下载进度
public static NitifierManager getInstance(Context context) {
mContext = context;
if (instance == null) {
instance = new NitifierManager();
}
if (nm == null) {
nm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
}
if (res == null) {
res = mContext.getResources();
}
return instance;
}
// 初始化下载进度
public void notifiDownloadFile() {
builder = new Notification.Builder(mContext);
builder.setSmallIcon(R.drawable.icon)
.setOnlyAlertOnce(true) //提示音只提示一次
.setContentTitle("xxx软件升级中")
.setContentText("进度...")
.setProgress(100,1,true);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
//提示音用IMPORTANCE_DEFAULT
NotificationChannel channel = new NotificationChannel("003","download_channel",NotificationManager.IMPORTANCE_DEFAULT);
nm.createNotificationChannel(channel);
builder.setChannelId("003");
}
Notification n = builder.build();
nm.notify(DOWN_LOAD_FILE,n);
}
// 更新进度
public void notifiProgress(int _progress) {
builder.setProgress(100,_progress,false);
Notification n = builder.build();
nm.notify(DOWN_LOAD_FILE,n);
}
//下载完成或下载失败 取消进度
public void cancelNotifiProgress(int state,String path,String apkFileName) {
if(state == DownStatus.DOWN_SUCCEED){
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
Uri uri=FileProvider.getUriForFile(mContext, mContext.getApplicationContext().getPackageName() + ".provider", new File(path + apkFileName));
//context.grantUriPermission(BuildConfig.APPLICATION_ID,uri,Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);//允许临时的读和写
intent.setDataAndType(uri,
"application/vnd.android.package-archive");
}else{
intent.setDataAndType(Uri.fromFile(new File(path + apkFileName)),
"application/vnd.android.package-archive");
}
nm.cancel(DOWN_LOAD_FILE);
builder.setProgress(0,0,false);
builder.setContentText("xxx软件下载完毕");
builder.setContentIntent(PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
Notification n = builder.build();
nm.notify(DOWN_LOAD_FILE,n);
}else{
nm.cancel(DOWN_LOAD_FILE);
builder.setProgress(0,0,false);
builder.setContentText("xxx软件下载失败");
Notification n = builder.build();
nm.notify(DOWN_LOAD_FILE,n);
}
}
}