常驻通知栏
不废话,上代码
//创建一个通知管理器
NotificationManager notificationManager= (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// 获取Notification实例
Notification notification=new NotificationCompat.Builder(this,channelId)
.setContentTitle("title")
.setContentText("content")
.setWhen(System.currentTimeMillis())
// .setAutoCancel(false) 点击通知栏后是否消失
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.icon))
// .setCustomContentView(remoteView) // 设置自定义的RemoteView,需要API最低为24
.setSmallIcon( R.mipmap.instant)
// 设置点击通知栏后跳转地址
.setContentIntent( PendingIntent.getActivity(this, 0, new Intent(this, SecondActivity.class), 0))
.build();
// 添加渠道
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
NotificationChannel channel = new NotificationChannel(channelId, "subscribeName", NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription("description");
notificationManager.createNotificationChannel(channel);
}
// 设置常驻 Flag
notification.flags = Notification.FLAG_ONGOING_EVENT;
//展示通知栏
notificationManager.notify(notificationId,notification);
展示效果如下
注意
在8.0以下手机是不用添加渠道的,但在之上不添加渠道不展示通知栏,9.0手机不设置setDescription()依旧不展示通知栏【华为mate10 9.0版本测试】;
自定义通知栏
有时间我们不想展示系统给的固定的展示效果,想自己写一个效果,只需要在代码中添加RemoteViews。
//通过xml创建RemoteViews,并且动态改变布局中的内容
RemoteViews remoteView = new RemoteViews(getPackageName(), R.layout.bar_notification);
remoteView.setImageViewResource(R.id.iv_icon, R.mipmap.ic_launcher);
remoteView.setTextViewText(R.id.tv_title, "my title");
remoteView.setTextViewText(R.id.tv_content, "my content");
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
String time = sdf.format(date);
remoteView.setTextViewText(R.id.tv_time, time);
并放开setCustomContentView()功能,这时Notification中设置的方法基本都不可用了,但是setSmallIcon()必须有(虽然没展示效果),不管是不是自定义布局都要有,否则就会报错。
关闭通知栏
/**
* 取消常驻通知
*
* @param context
*/
public static void delNotification(Context context) {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(notificationId);
}
这里的notificationId即为最开始notificationManager.notify(notificationId,notification)中的notificationId。
手机设置里修改通知状态
public void setting(View view) {
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
intent.putExtra(Settings.EXTRA_CHANNEL_ID, channelId);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
startActivity(intent);
}
这里的channelId 就是我们在添加渠道时设置的Id
防止多创建界面
我们在 设置setContentIntent方法时是直接getActivity,这样会导致new出一个activity,点击返回键会返回到原来未点通知之前的界面
/**
* 构造一个合成的回退栈,主要用于跨任务导航
*/
Intent resultIntent = new Intent(context, MainActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
将resultPendingIntent 传入.setContentIntent()即可
其实到这里,基本效果已经好了,市面上大部分APP的通知栏都是系统自带的效果,自定义的都不是,就算是自定义的大部分也不需要进行网络图片的加载!!!
加载网路数据
既然加了图片,很大可能是从服务器发送来的照片
这时setImageViewResource()功能就不行了,还好,remoteView里有另外两种方法
remoteView.setImageViewUri(R.id.iv_icon, uri);
remoteView.setImageViewBitmap(R.id.iv_icon,bitmap);
看源码发现方式与ImageView的setImageURI(Uri)和setImageBitmap(Bitmap)类似,
这就好办了
Uri uri = Uri.parse("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1565676565588&di=14318ae342c65b768a272ef158a6fd78&imgtype=0&src=http%3A%2F%2Fpic1.16pic.com%2F00%2F51%2F76%2F16pic_5176356_b.jpg");
remoteView.setImageViewUri(R.id.iv_click, uri);
发现一点也不行,以为是图片过大或者图片是jpg 格式的问题,后来才发现这个方式原来针对的是本地图片,都说了类似imageview了,狠狠地打脸啊,在此记录一下!!!
imageview.setImageURI(Uri.parse("file:///assets/xxx.jpg"))
相对应的
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.a)
这也是本地图片,既然写到这里记录一下图片转化bitmap的方法吧,省得自己再被打脸
// 一、加载R文件下的图片
//最简单的方式
Bitmap bitmap =BitmapFactory.decodeResource(getResources(), R.drawable.loading);
//二、文件流的方式转化bitmap
FileInputStream fis = null;
try {
fis = new FileInputStream(dir + "/" + fileName);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Bitmap bitmap = BitmapFactory.decodeStream(fis);
// 三、网络url转化为bitmap
URL url = new URL(imgUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
InputStream is = conn.getInputStream();
Bitmap bm = BitmapFactory.decodeStream(is);
前两种转化为bitmap时,都可能会因为图片过大导致溢出,
第一种方式可以参照大佬android避免decodeResource图片时占用太大的内存
/**
* 我们知道调用BitmapFactory.decodeResource时,如果手机屏幕的密度很大时,如果只是在hdpi放了图片, decode出来的* bitmap会自动的scale放大。 而且如果按照ARGB_8888模式decode的话一个像素需要4个字节,这样343*433分辨率的图* * 片decode大概会占用2M多内存。 所以从2方面控制,一个是禁止scale, 一个是使用ALPHA_8模式decode。注意:这里* * 需要看看效果是否ok, 因为使用低质量的模式decode图片可能会修饰一些图片的细节。
* 因为目前我们只有一套资源文件,全都放在hdpi下面,这样如果是遇到高密度手机, 系统会按照
* scale = (float) targetDensity / density 把图片放到几倍,这样会使得在高密度手机上经常会发生OOM。
*
* 这个方法用来解决在如果密度大于hdpi(240)的手机上,decode资源文件被放大scale,内容浪费的问题。
*
* @param resources
* @param id
* @return
*/
public static Bitmap decodeResource(Resources resources, int id) {
int densityDpi = resources.getDisplayMetrics().densityDpi;
Bitmap bitmap;
TypedValue value = new TypedValue();
resources.openRawResource(id, value);
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inPreferredConfig = Bitmap.Config.ALPHA_8;
if (densityDpi > DisplayMetrics.DENSITY_HIGH) {
opts.inTargetDensity = value.density;
bitmap = BitmapFactory.decodeResource(resources, id, opts);
}else{
bitmap = BitmapFactory.decodeResource(resources, id);
}
return bitmap;
}
第二种方式可以参照大佬图片文件和Bitmap之间的转换
言归正传,我们以remoteView.setImageViewBitmap(R.id.iv_icon,bitmap)为例,这里需要设置网络图片转为bitmap的方法,也就是如上第三种方式,图片加载需要设置异步,这里使用Thread+Handler模式,代码如下:
public void setNotification(Context mContext) {
context = mContext;
final String iconUrl = SpUtils.getSpUtils(context).getNotificationIconUrl();
if (TextUtils.isEmpty(iconUrl)) {
return;
}
new Thread() {
@Override
public void run() {
getBitmap(iconUrl);
}
}.start();
}
/**
* @param
* @author admin
* @Description: 获取Bitmap<br/>
*/
private void getBitmap(String resUrl) {
URL fileUrl = null;
Bitmap bitmap = null;
try {
fileUrl = new URL(resUrl);
} catch (MalformedURLException e) {
e.printStackTrace();
}
try {
HttpURLConnection conn = (HttpURLConnection) (fileUrl != null ? fileUrl
.openConnection() : null);
assert conn != null;
conn.setDoInput(true);
conn.connect();
InputStream is = conn.getInputStream();
bitmap = BitmapFactory.decodeStream(is);
is.close();
Message message = Message.obtain();
message.obj = bitmap;
handler.sendMessage(message);
} catch (Exception e) {
e.printStackTrace();
}
}
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
showNotivication((Bitmap) msg.obj);
}
};
到这里,我们需要的bitmap即可在最开始的代码里通过 showNotivication((Bitmap) msg.obj) 引用了。
到这里网络加载也基本上可以结束了。但是,但是,当发现公司要求通知栏里展示两张图片的时候,我又纳闷了,用Thread+Handler,最常见的是异步加载一个请求呀!没见过new 两个 thread的(据说new两个会数据会串,俺也不知道,俺也没地方问),就算可以,讲个异步操作都执行完在去执行show通知栏会不会很麻烦(其实不算麻烦,既然是异步,总有先后,当后加载好的图片加载完成后再去调用通知栏方法就好了嘛)?当然也可以用AsyncTask加载更多图片