常駐通知欄
不廢話,上代碼
//創建一個通知管理器
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加載更多圖片