【问题标题】:Large Icon Bitmap appears as white square in notification?大图标位图在通知中显示为白色方块?
【发布时间】:2017-02-20 22:03:18
【问题描述】:

我遇到了这个问题,我从通知中使用的 URL 生成了 Bitmap。但是,在我的手机上,Bitmap 显示为一个白色的小方块。我查了一下,发现很多帖子都在谈论它:Icon not displaying in notification: white square shown instead

我确信我的Small Icon 通知确实是透明的。但是,对于Large Icon,我意识到Large Icon 不能透明,因为它实际上是我从URL 生成的Bitmap。那么我该如何解决这个问题并确保图像正确渲染,而不是让Large Icon 显示为白色方块?这是我的尝试:

NotificationService.java:

    NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context)
            .setContentTitle(getString(R.string.app_name))
            .setContentText(remoteMessage.getNotification().getBody())
            .setTicker(remoteMessage.getFrom() + " has responded!")
            .setLargeIcon(AndroidUtils.getBitmapFromURL(remoteMessage.getNotification().getIcon()))
            .setAutoCancel(true)
            .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
            .setStyle(new NotificationCompat.BigTextStyle().bigText(remoteMessage.getNotification().getBody()))
            .setSmallIcon(R.drawable.ic_tabs_notification_transparent);

AndroidUtils.java:

public static Bitmap getBitmapFromURL(String userId) {
        try {
            URL imgUrl = new URL("https://graph.facebook.com/" + userId + "/picture?type=large");
            InputStream in = (InputStream) imgUrl.getContent();
            Bitmap  bitmap = BitmapFactory.decodeStream(in);
            Bitmap output;
            Rect srcRect;
            if (bitmap.getWidth() > bitmap.getHeight()) {
                output = Bitmap.createBitmap(bitmap.getHeight(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
                srcRect = new Rect((bitmap.getWidth()-bitmap.getHeight())/2, 0, bitmap.getWidth()+(bitmap.getWidth()-bitmap.getHeight())/2, bitmap.getHeight());
            } else {
                output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getWidth(), Bitmap.Config.ARGB_8888);
                srcRect = new Rect(0, (bitmap.getHeight()-bitmap.getWidth())/2, bitmap.getWidth(), bitmap.getHeight()+(bitmap.getHeight()-bitmap.getWidth())/2);
            }

            Canvas canvas = new Canvas(output);

            final int color = 0xff424242;
            final Paint paint = new Paint();
            final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());

            float r;

            if (bitmap.getWidth() > bitmap.getHeight()) {
                r = bitmap.getHeight() / 2;
            } else {
                r = bitmap.getWidth() / 2;
            }

            paint.setAntiAlias(true);
            canvas.drawARGB(0, 0, 0, 0);
            paint.setColor(color);
            canvas.drawCircle(r, r, r, paint);
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
            canvas.drawBitmap(bitmap, srcRect, rect, paint);
            return output;
        } catch (IOException e) {
            FirebaseCrash.report(e);
            return null;
        }

图片显示我的问题:

编辑:Build.gradle 文件显示:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }
    defaultConfig {
        applicationId '<ID>'
        multiDexEnabled true
        minSdkVersion 21
        targetSdkVersion 23
        versionCode 12
        versionName ".12"
        signingConfig signingConfigs.Tabs
    }
    buildTypes {
        release {
            minifyEnabled false
            shrinkResources false
            zipAlignEnabled true
            debuggable false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.Tabs
        }
        debug {
            applicationIdSuffix ".debug"
            debuggable true
            minifyEnabled false
            signingConfig signingConfigs.Tabs
        }
    }
    //DatabaseReference stuff
    packagingOptions {
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/LICENSE-FIREBASE.txt'
        exclude 'META-INF/NOTICE'
    }
    dexOptions {
        javaMaxHeapSize "4g"
    }
    productFlavors {
    }
}

【问题讨论】:

  • 显示您的应用级别的 buil.gradle 文件...
  • @NikunjParadva 我刚刚上传了它的所有细节
  • 请更改targetsdkversion 20,然后再次运行
  • 你满意吗?
  • @NikunjParadva 问题是,如果我将 targetSdkVersion 设置为 20,我的一些功能就会丢失。例如,NestedScrollView 不起作用,我也无法更改状态栏的状态。还有其他解决方法吗?或者为什么 targetSdkVersion 20 如此重要?

标签: java android bitmap icons android-notifications


【解决方案1】:

根据setColor() documentation

参数

argb -The accent color to use

您传入的 2 不是有效的 ARGB 颜色,因此您的小图标的背景颜色显示不正确。相反,请选择有效的 ARGB 颜色。

如果你有想要使用的颜色资源,可以使用代码如

.setColor(context.getResources().getColor(R.color.notification_color))

另外,注意 Android 5.0 的状态变化:

更新或删除涉及颜色的资产。系统会忽略操作图标和主通知图标中的所有非 Alpha 通道。您应该假设这些图标仅是 Alpha 版。系统以白色绘制通知图标,以深灰色绘制操作图标。

您的小图标应该完全是白色和透明的 - 您可以使用 Notification Icon Generator 等工具生成适当的图标。

或者你可以试试这个:

int icon = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? R.drawable.your_logo_for_Kitkat : R.mipmap.your_logo_for_Lolipop_and_uper_version;
    NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
            .setSmallIcon(icon)
            .setContentTitle(remoteMessage.getData().get("title"))
            .setContentText(remoteMessage.getData().get("shortDescription"))
            .setAutoCancel(true)
            .setSound(defaultSoundUri)
            .setColor(Color.RED)
            .setStyle(notiStyle)
            .setContentIntent(pendingIntent);

对于 Url 图片加载:

 private class sendNotification extends AsyncTask<String, Void, Bitmap> {

        Context ctx;
        String message;

        public sendNotification(Context context) {
            super();
            this.ctx = context;
        }

        @Override
        protected Bitmap doInBackground(String... params) {

            InputStream in;
            message = params[0] + params[1];
            try {

 URL url = new URL(params[2]);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setDoInput(true);
        connection.connect();
        in = connection.getInputStream();
        Bitmap myBitmap = BitmapFactory.decodeStream(in);
        return myBitmap;




            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onPostExecute(Bitmap result) {

            super.onPostExecute(result);
            try {
                NotificationManager notificationManager = (NotificationManager) ctx
                        .getSystemService(Context.NOTIFICATION_SERVICE);

                Intent intent = new Intent(ctx, NotificationsActivity.class);
                intent.putExtra("isFromBadge", false);


                Notification notification = new Notification.Builder(ctx)
                        .setContentTitle(
                                ctx.getResources().getString(R.string.app_name))
                        .setContentText(message)
                        .setSmallIcon(R.drawable.ic_launcher)
                        .setLargeIcon(result).build();

                // hide the notification after its selected
                notification.flags |= Notification.FLAG_AUTO_CANCEL;

                notificationManager.notify(1, notification);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

【讨论】:

  • setColor 是否应该用于位图而不是smallIcon?我无法正确渲染位图。
  • 您面临什么样的问题?以及显示什么样的错误?
  • 问题是当我向朋友的手机发送通知时没有错误显示。只是当我尝试使用位图显示照片时,它只是显示为一个白色方块,我真的不知道为什么。
  • 我明白你在做什么,但是如果我没有我的可绘制图标作为资源怎么办?如果图片来自我上面发布的 URL 怎么办?
  • @user1871869 现在看到我更新的答案,它从 URL 加载!希望它对你有用..谢谢:)
【解决方案2】:

因为谷歌在 API 21+ 中更改了通知样式,建议使用white notification icon

虽然您可以在所有其他设备上使用 21 岁以上和旧图标上的剪影图标,但如下所示

private int getNotificationIcon() {
    boolean icon = (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP);
    return icon ? R.drawable.silhouette : R.drawable.ic_launcher;
}

这是实际的通知生成器代码:

Notification builder = new Notification.Builder(context)
        .setSmallIcon(getNotificationIcon())
        .build();

【讨论】:

  • 我确实将我的小图标图像设置为透明,但我不明白为什么我的Large Icon 显示不正确。大图标也需要透明吗?那么,当我绘制我的位图时,它是否也需要是透明的?
【解决方案3】:

你可以为不同的版本使用不同的图标:

int bigIcon = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? R.drawable.logo_who_is_not_white: R.drawable.logo_who_is_in_whit_tint;

【讨论】:

    【解决方案4】:
    try {
                NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
                mBuilder.setSmallIcon(getNotificationIcon());
                Bitmap icon = BitmapFactory.decodeResource(CXGcmListenerService.this.getResources(), R.drawable.ic_launcher);
                mBuilder.setLargeIcon(icon);
                //Define sound URI
                Uri soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
                mBuilder.setContentIntent(pendingIntent);
                mBuilder.setContentText(msg);
                mBuilder.setStyle(new NotificationCompat.BigTextStyle()
                        .bigText(msg));
                mBuilder.setContentTitle(getString(R.string.app_name));
                mBuilder.getNotification().flags |= Notification.FLAG_AUTO_CANCEL;
                mBuilder.setAutoCancel(true);
                mBuilder.setSound(soundUri); //This sets the sound to play
                Intent intent = new Intent(CXGcmListenerService.this, CXMainActivity.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                PendingIntent notificationIntent = PendingIntent.getActivity(this, 0, intent, 0);
                mBuilder.setContentIntent(notificationIntent);
    
                NotificationManager notifyManager = (NotificationManager) CXGcmListenerService.this.getSystemService(Context.NOTIFICATION_SERVICE);
                NOTIFICATION_ID++;
                notifyManager.notify("" + System.currentTimeMillis(), NOTIFICATION_ID, mBuilder.build());
            } catch (Resources.NotFoundException e) {
                CXLog.e(TAG, "" + e.getLocalizedMessage(), e);
            }
    

    请尝试此操作以获取通知中的图标。

    【讨论】:

      【解决方案5】:

      我认为问题在于图像大小,状态栏图标大小如下 -

      ldpi - 18 x 18 px
      mdpi - 24 x 24 px
      hdpi - 36 x 36 px
      xhdpi - 48 x 48 px
      

      你也可以看看this developer doc

      您需要将接收到的位图的尺寸更改为上述尺寸,它应该可以工作。

      【讨论】:

        【解决方案6】:

        Lollipop 和更高版本的 android API 级别的通知发生了变化。要解决此问题,您可以按照以下方式:

        方式 1: 在应用模块的 build.gradle 中将 targetSdkVersion 从 23 更改为 19。 这将解决这个问题。

        注意:也许这个解决方案会在你的构建系统中产生问题(我不希望你用这种方式,但你可以用这种方式检查你的问题)

        方式 2: 将通知图标更改为黑白图标。因为通知生成器不支持棒棒糖 API 级别的颜色图标。

        Android Studio中黑白图标的获取方式:

        1. 右键单击 Drawable 文件夹 > 添加图像资源(任何资源文件夹都应该这样做)

        2. 点击顶部下拉列表中的通知图标(默认为启动器图标)

        3. 选择图片>浏览图片>点击确定

        这是正常的,因为 Lollipop SDK (API 21 - Ver 5.0.1) 只允许这种颜色方案

        或者你可以从这里生成:http://romannurik.github.io/AndroidAssetStudio/icons-notification.html

        【讨论】:

        • 大图标也需要透明吗?那么,当我绘制我的位图时,它是否也需要是透明的?
        • 无论是设置小图标还是大图标,都应该对病房上的 Android API 21 透明。
        【解决方案7】:

        Lolipop有这个问题存在,你看看其他Lolipop之前的手机没有问题。

        请参考以下链接

        https://material.google.com/style/icons.html

        https://developer.android.com/about/versions/android-5.0-changes.html

        否则你可以更改编译好的SDK

        【讨论】:

          【解决方案8】:

          我认为您一直在从 URL 下载图像,对吧?因此,请放置一个 Asynctask 以从 url 下载图像,然后使用通知生成器显示通知,如下所示:https://stackoverflow.com/a/24866080/6452886

          【讨论】: