常駐通知欄

常駐通知欄

不廢話,上代碼

//創建一個通知管理器
  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加載更多圖片

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章