常驻通知栏

本文介绍如何在Android中实现自定义常驻通知栏,包括关闭通知、设置通知状态、防止多创建界面,并详细讲解了在网络环境下加载图片的挑战与解决方案,特别提到了在9.0手机上的兼容问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

常驻通知栏

不废话,上代码

//创建一个通知管理器
  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加载更多图片

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值