【问题标题】:Is there a way to add a badge to an application icon in Android?有没有办法将徽章添加到 Android 中的应用程序图标?
【发布时间】:2011-02-23 17:37:52
【问题描述】:

在 iPhone 上,您可以为应用程序图标添加编号徽章。在黑莓上,我在程序中成功地将图像绘制到应用程序的图标上。我也想为 Android 做这个。我不想使用通知栏,因为它不需要立即通知。相反,我只是希望用户能够通过查看应用程序图标来了解应用程序中有多少新消息。

【问题讨论】:

  • 这是我找到的最好的解决方案。 stackoverflow.com/a/24804467/957954
  • 我的三星 note 5 为 Skype、Whatsapp 和 Facebook 展示了它们。所以一定有可能。

标签: android icons badge


【解决方案1】:

从 API 26 开始,现在是 officially supported

从 8.0(API 级别 26)开始,当相关应用有活动通知时,通知标记(也称为通知点)会出现在启动器图标上。用户可以长按应用图标以显示通知(以及任何应用快捷方式),如图 1 所示。

这些点默认显示在支持它们的启动器应用中,您的应用无需执行任何操作。但是,在某些情况下,您可能不希望显示通知点,或者您希望准确控制要显示的通知。

要设置自定义号码,请在通知上调用 setNumber():

mNotification.setNumber(messageCount)

【讨论】:

  • 你是如何把它放在应该显示它的一边的?不是默认启动器的应用能否获取此信息?
【解决方案2】:

华硕设备:

public static class AsusHomeBadger implements Badger {

    private static final String INTENT_ACTION = "android.intent.action.BADGE_COUNT_UPDATE";
    private static final String INTENT_EXTRA_BADGE_COUNT = "badge_count";
    private static final String INTENT_EXTRA_PACKAGENAME = "badge_count_package_name";
    private static final String INTENT_EXTRA_ACTIVITY_NAME = "badge_count_class_name";

    @Override
    public void executeBadge(int badgeCount) {
        final Intent intent = new Intent(INTENT_ACTION);
        intent.putExtra(INTENT_EXTRA_BADGE_COUNT, badgeCount);
        intent.putExtra(INTENT_EXTRA_PACKAGENAME, componentName.getPackageName());
        intent.putExtra(INTENT_EXTRA_ACTIVITY_NAME, componentName.getClassName());
        intent.putExtra("badge_vip_count", 0);
        if (canResolveBroadcast(intent)) {
            AndroidUtilities.runOnUIThread(new Runnable() {
                @Override
                public void run() {
                    ApplicationLoader.applicationContext.sendBroadcast(intent);
                }
            });
        }
    }

    @Override
    public List<String> getSupportLaunchers() {
        return Arrays.asList("com.asus.launcher");
    }
}

华为设备:

public static class HuaweiHomeBadger implements Badger {

    @Override
    public void executeBadge(int badgeCount) {
        final Bundle localBundle = new Bundle();
        localBundle.putString("package", ApplicationLoader.applicationContext.getPackageName());
        localBundle.putString("class", componentName.getClassName());
        localBundle.putInt("badgenumber", badgeCount);
        AndroidUtilities.runOnUIThread(new Runnable() {
            @Override
            public void run() {
                try {
                    ApplicationLoader.applicationContext.getContentResolver().call(Uri.parse("content://com.huawei.android.launcher.settings/badge/"), "change_badge", null, localBundle);
                } catch (Exception e) {
                    FileLog.e(e);
                }
            }
        });
    }

    @Override
    public List<String> getSupportLaunchers() {
        return Arrays.asList(
                "com.huawei.android.launcher"
        );
    }
}

HTC 设备:

public static class NewHtcHomeBadger implements Badger {

    public static final String INTENT_UPDATE_SHORTCUT = "com.htc.launcher.action.UPDATE_SHORTCUT";
    public static final String INTENT_SET_NOTIFICATION = "com.htc.launcher.action.SET_NOTIFICATION";
    public static final String PACKAGENAME = "packagename";
    public static final String COUNT = "count";
    public static final String EXTRA_COMPONENT = "com.htc.launcher.extra.COMPONENT";
    public static final String EXTRA_COUNT = "com.htc.launcher.extra.COUNT";

    @Override
    public void executeBadge(int badgeCount) {

        final Intent intent1 = new Intent(INTENT_SET_NOTIFICATION);
        intent1.putExtra(EXTRA_COMPONENT, componentName.flattenToShortString());
        intent1.putExtra(EXTRA_COUNT, badgeCount);

        final Intent intent = new Intent(INTENT_UPDATE_SHORTCUT);
        intent.putExtra(PACKAGENAME, componentName.getPackageName());
        intent.putExtra(COUNT, badgeCount);

        if (canResolveBroadcast(intent1) || canResolveBroadcast(intent)) {
            AndroidUtilities.runOnUIThread(new Runnable() {
                @Override
                public void run() {
                    ApplicationLoader.applicationContext.sendBroadcast(intent1);
                    ApplicationLoader.applicationContext.sendBroadcast(intent);
                }
            });
        }
    }

    @Override
    public List<String> getSupportLaunchers() {
        return Arrays.asList("com.htc.launcher");
    }
}

三星设备:

public static class SamsungHomeBadger implements Badger {
    private static final String CONTENT_URI = "content://com.sec.badge/apps?notify=true";
    private static final String[] CONTENT_PROJECTION = new String[]{"_id","class"};

    private static DefaultBadger defaultBadger;

    @Override
    public void executeBadge(int badgeCount) {
        try {
            if (defaultBadger == null) {
                defaultBadger = new DefaultBadger();
            }
            defaultBadger.executeBadge(badgeCount);
        } catch (Exception ignore) {

        }

        Uri mUri = Uri.parse(CONTENT_URI);
        ContentResolver contentResolver = ApplicationLoader.applicationContext.getContentResolver();
        Cursor cursor = null;
        try {
            cursor = contentResolver.query(mUri, CONTENT_PROJECTION, "package=?", new String[]{componentName.getPackageName()}, null);
            if (cursor != null) {
                String entryActivityName = componentName.getClassName();
                boolean entryActivityExist = false;
                while (cursor.moveToNext()) {
                    int id = cursor.getInt(0);
                    ContentValues contentValues = getContentValues(componentName, badgeCount, false);
                    contentResolver.update(mUri, contentValues, "_id=?", new String[]{String.valueOf(id)});
                    if (entryActivityName.equals(cursor.getString(cursor.getColumnIndex("class")))) {
                        entryActivityExist = true;
                    }
                }

                if (!entryActivityExist) {
                    ContentValues contentValues = getContentValues(componentName, badgeCount, true);
                    contentResolver.insert(mUri, contentValues);
                }
            }
        } finally {
            close(cursor);
        }
    }

    private ContentValues getContentValues(ComponentName componentName, int badgeCount, boolean isInsert) {
        ContentValues contentValues = new ContentValues();
        if (isInsert) {
            contentValues.put("package", componentName.getPackageName());
            contentValues.put("class", componentName.getClassName());
        }

        contentValues.put("badgecount", badgeCount);

        return contentValues;
    }

    @Override
    public List<String> getSupportLaunchers() {
        return Arrays.asList(
                "com.sec.android.app.launcher",
                "com.sec.android.app.twlauncher"
        );
    }
}

索尼设备:

public static class SonyHomeBadger implements Badger {

    private static final String INTENT_ACTION = "com.sonyericsson.home.action.UPDATE_BADGE";
    private static final String INTENT_EXTRA_PACKAGE_NAME = "com.sonyericsson.home.intent.extra.badge.PACKAGE_NAME";
    private static final String INTENT_EXTRA_ACTIVITY_NAME = "com.sonyericsson.home.intent.extra.badge.ACTIVITY_NAME";
    private static final String INTENT_EXTRA_MESSAGE = "com.sonyericsson.home.intent.extra.badge.MESSAGE";
    private static final String INTENT_EXTRA_SHOW_MESSAGE = "com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE";

    private static final String PROVIDER_CONTENT_URI = "content://com.sonymobile.home.resourceprovider/badge";
    private static final String PROVIDER_COLUMNS_BADGE_COUNT = "badge_count";
    private static final String PROVIDER_COLUMNS_PACKAGE_NAME = "package_name";
    private static final String PROVIDER_COLUMNS_ACTIVITY_NAME = "activity_name";
    private static final String SONY_HOME_PROVIDER_NAME = "com.sonymobile.home.resourceprovider";
    private final Uri BADGE_CONTENT_URI = Uri.parse(PROVIDER_CONTENT_URI);

    private static AsyncQueryHandler mQueryHandler;

    @Override
    public void executeBadge(int badgeCount) {
        if (sonyBadgeContentProviderExists()) {
            executeBadgeByContentProvider(badgeCount);
        } else {
            executeBadgeByBroadcast(badgeCount);
        }
    }

    @Override
    public List<String> getSupportLaunchers() {
        return Arrays.asList("com.sonyericsson.home", "com.sonymobile.home");
    }

    private static void executeBadgeByBroadcast(int badgeCount) {
        final Intent intent = new Intent(INTENT_ACTION);
        intent.putExtra(INTENT_EXTRA_PACKAGE_NAME, componentName.getPackageName());
        intent.putExtra(INTENT_EXTRA_ACTIVITY_NAME, componentName.getClassName());
        intent.putExtra(INTENT_EXTRA_MESSAGE, String.valueOf(badgeCount));
        intent.putExtra(INTENT_EXTRA_SHOW_MESSAGE, badgeCount > 0);
        AndroidUtilities.runOnUIThread(new Runnable() {
            @Override
            public void run() {
                ApplicationLoader.applicationContext.sendBroadcast(intent);
            }
        });
    }

    private void executeBadgeByContentProvider(int badgeCount) {
        if (badgeCount < 0) {
            return;
        }

        if (mQueryHandler == null) {
            mQueryHandler = new AsyncQueryHandler(ApplicationLoader.applicationContext.getApplicationContext().getContentResolver()) {

                @Override
                public void handleMessage(Message msg) {
                    try {
                        super.handleMessage(msg);
                    } catch (Throwable ignore) {

                    }
                }
            };
        }
        insertBadgeAsync(badgeCount, componentName.getPackageName(), componentName.getClassName());
    }

    private void insertBadgeAsync(int badgeCount, String packageName, String activityName) {
        final ContentValues contentValues = new ContentValues();
        contentValues.put(PROVIDER_COLUMNS_BADGE_COUNT, badgeCount);
        contentValues.put(PROVIDER_COLUMNS_PACKAGE_NAME, packageName);
        contentValues.put(PROVIDER_COLUMNS_ACTIVITY_NAME, activityName);
        mQueryHandler.startInsert(0, null, BADGE_CONTENT_URI, contentValues);
    }

    private static boolean sonyBadgeContentProviderExists() {
        boolean exists = false;
        ProviderInfo info = ApplicationLoader.applicationContext.getPackageManager().resolveContentProvider(SONY_HOME_PROVIDER_NAME, 0);
        if (info != null) {
            exists = true;
        }
        return exists;
    }
}

小米设备:

public static class XiaomiHomeBadger implements Badger {

    public static final String INTENT_ACTION = "android.intent.action.APPLICATION_MESSAGE_UPDATE";
    public static final String EXTRA_UPDATE_APP_COMPONENT_NAME = "android.intent.extra.update_application_component_name";
    public static final String EXTRA_UPDATE_APP_MSG_TEXT = "android.intent.extra.update_application_message_text";

    @Override
    public void executeBadge(int badgeCount) {
        try {
            Class miuiNotificationClass = Class.forName("android.app.MiuiNotification");
            Object miuiNotification = miuiNotificationClass.newInstance();
            Field field = miuiNotification.getClass().getDeclaredField("messageCount");
            field.setAccessible(true);
            field.set(miuiNotification, String.valueOf(badgeCount == 0 ? "" : badgeCount));
        } catch (Throwable e) {
            final Intent localIntent = new Intent(INTENT_ACTION);
            localIntent.putExtra(EXTRA_UPDATE_APP_COMPONENT_NAME, componentName.getPackageName() + "/" + componentName.getClassName());
            localIntent.putExtra(EXTRA_UPDATE_APP_MSG_TEXT, String.valueOf(badgeCount == 0 ? "" : badgeCount));
            if (canResolveBroadcast(localIntent)) {
                AndroidUtilities.runOnUIThread(new Runnable() {
                    @Override
                    public void run() {
                        ApplicationLoader.applicationContext.sendBroadcast(localIntent);
                    }
                });
            }
        }
    }

    @Override
    public List<String> getSupportLaunchers() {
        return Arrays.asList(
                "com.miui.miuilite",
                "com.miui.home",
                "com.miui.miuihome",
                "com.miui.miuihome2",
                "com.miui.mihome",
                "com.miui.mihome2"
        );
    }
}

【讨论】:

    【解决方案3】:

    ShortcutBadger 库使之成为可能,并与 LG、Sony、Samsung、HTC 和其他自定义启动器一起使用。

    它甚至可以在纯 Android 设备桌面上显示徽章计数。

    在应用程序图标中更新徽章计数就像调用一样简单:

    int badgeCount = 1;
    ShortcutBadger.setBadge(getApplicationContext(), badgeCount);
    

    它包含一个演示应用程序,可让您测试其行为。

    您也可以尝试使用 activity-alias 来执行此操作,但在此您需要使用徽章值创建不同的图标,这在您需要在 2 个不同的 App 图标之间切换(需要创建不同的活动-显示不同图标的别名,即更多图标 = 更多活动别名)。

    【讨论】:

    • 我已经下载了 ShortcutBadger 的 zip 文件并导入到我的 AndroidStudio 中。当我在提供的 EditText 中键入 10 时,它将显示一条 Toast 消息。但是图标上没有显示任何内容。我们必须做什么
    • 对不起,不明白你的意思..你能说清楚你到底想要什么。,........因为我已经指定了如何使用 shorcutBadger 来设置徽章。
    • ShortcutBadger 导入 Android Studio 后运行成功。我的问题是“当我在 EditText 中输入一些数字并单击 SET BADGE 按钮时,我无法在 Launcher 中的 App 上看到任何徽章”。为什么我看不到这个徽章。
    • 你在哪个设备上测试......实际上这个库只在选定的设备上工作......
    • 我在 Emulator API 23、真实设备(Moto g、Xolo)上运行。
    【解决方案4】:

    以下是如何做到这一点:

    我认为在 LG 启动器上也有办法做到这一点,但还没有找到方法。

    【讨论】:

      【解决方案5】:

      这也适用于索尼的 Xperia Home。我已经在博客上写了here,但重要的部分如下。 Sony 设备使用名为 BadgeReciever 的类。

      1. 在清单文件中声明com.sonyericsson.home.permission.BROADCAST_BADGE 权限:

      2. Intent 广播到BadgeReceiver

        Intent intent = new Intent();
        
        intent.setAction("com.sonyericsson.home.action.UPDATE_BADGE");
        intent.putExtra("com.sonyericsson.home.intent.extra.badge.ACTIVITY_NAME", "com.yourdomain.yourapp.MainActivity");
        intent.putExtra("com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE", true);
        intent.putExtra("com.sonyericsson.home.intent.extra.badge.MESSAGE", "99");
        intent.putExtra("com.sonyericsson.home.intent.extra.badge.PACKAGE_NAME", "com.yourdomain.yourapp");
        
        sendBroadcast(intent);
        
      3. 完成。广播此Intent 后,启动器应在您的应用程序图标上显示一个徽章。

      4. 要再次移除徽章,只需发送一个新广播,这次将 SHOW_MESSAGE 设置为 false:

        intent.putExtra("com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE", false);
        

      我已排除有关如何找到此问题的详细信息以保持答案简短,但所有内容都可以在博客中找到。对某人来说可能是一本有趣的书。

      我还发布了一个关于此 here 的单独 SO 问题,一旦允许,我将在那里添加完整的答案(需要 10 名声望才能在 8 小时内回答我自己的问题)。

      【讨论】:

        【解决方案6】:

        不幸的是,Android 不允许更改应用程序图标,因为一旦程序编译完成,它就被密封在 APK 中。无法以编程方式将其更改为“drawable”。

        您可以通过使用小部件而不是图标来实现您的目标。小部件是高度可定制的,可以做你想做的。

        这里有一个关于 iPhone 图标通知和使用小部件的区别的简短讨论:

        http://www.cnet.com/8301-19736_1-10278814-251.html

        您会注意到,使用小部件或图标之间几乎没有区别,因为它们可以具有相同的大小和外观。

        【讨论】:

        • 你确定没有办法劫持启动器的主屏幕并将徽章位 blt 到图像上吗?我想要相同的功能,而 appwidget 是针对不同问题的不同解决方案。任何人都可以扔掉我的小部件,但除非他们卸载该应用程序,否则该图标将永远保持真实。自从在 Eclair/Froyo 时代问到这个问题后,Gingerbread 的操作系统级别是否有任何变化??
        • 诸如消息之类的股票应用怎么样? BadgeProvider呢?
        • 请注意这个答案的日期!早在 2010 年,Android 就大不一样了
        猜你喜欢
        • 2021-08-26
        • 1970-01-01
        • 2021-10-04
        • 1970-01-01
        • 2021-07-23
        • 2014-11-09
        • 2013-10-05
        • 2018-01-04
        • 1970-01-01
        相关资源
        最近更新 更多