You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Android Kotlin应用中实现通知点与数字角标双样式支持?

Android Kotlin实现同时支持点样式和数字样式通知角标

Android 8.0+系统默认支持点样式通知角标,但数字样式角标没有统一系统API,需要针对不同手机厂商做适配。以下是具体实现方案:

1. 原生点样式角标实现

原生点样式通过BadgeDrawable实现,仅控制角标是否显示,代码示例:

import android.graphics.drawable.BadgeDrawable
import android.os.Build
import android.view.View
import androidx.core.view.ViewCompat

fun setDotBadge(view: View, count: Int) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val badge = BadgeDrawable.create(view.context)
        badge.isVisible = count > 0
        ViewCompat.setBadgeDrawable(view, badge)
    }
}

2. 数字样式角标的厂商适配

数字角标需调用各厂商私有API,以下是主流厂商的Kotlin适配代码,封装为工具类统一处理:

封装工具类 BadgeUtils.kt

import android.app.NotificationManager
import android.content.Context
import android.content.Intent
import android.os.Build
import android.widget.RemoteViews

object BadgeUtils {

    // 更新角标(自动适配厂商)
    fun updateBadge(context: Context, unreadCount: Int) {
        when {
            isMiui() -> setMiuiBadge(context, unreadCount)
            isHuawei() -> setHuaweiBadge(context, unreadCount)
            isSamsung() -> setSamsungBadge(context, unreadCount)
            isOppo() -> setOppoBadge(context, unreadCount)
            isVivo() -> setVivoBadge(context, unreadCount)
            else -> setDotBadgeFallback(context, unreadCount)
        }
    }

    // 小米适配
    private fun setMiuiBadge(context: Context, count: Int) {
        val launcherComponent = context.packageManager.getLaunchIntentForPackage(context.packageName)?.component ?: return
        val intent = Intent("android.intent.action.APPLICATION_MESSAGE_UPDATE")
        intent.putExtra("android.intent.extra.update_application_component_name", launcherComponent.flattenToString())
        intent.putExtra("android.intent.extra.update_application_message_text", count)
        context.sendBroadcast(intent)
    }

    // 华为适配
    private fun setHuaweiBadge(context: Context, count: Int) {
        val launcherComponent = context.packageManager.getLaunchIntentForPackage(context.packageName)?.component ?: return
        val intent = Intent("com.huawei.android.launcher.action.CHANGE_BADGE")
        intent.putExtra("package", context.packageName)
        intent.putExtra("class", launcherComponent.className)
        intent.putExtra("badgenumber", count)
        context.sendBroadcast(intent)
    }

    // 三星适配
    private fun setSamsungBadge(context: Context, count: Int) {
        val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        // 创建低优先级通知渠道
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = notificationManager.getNotificationChannel("badge_channel")
                ?: notificationManager.createNotificationChannel(
                    android.app.NotificationChannel("badge_channel", "Badge Channel", NotificationManager.IMPORTANCE_LOW)
                )
        }
        // 构建空通知承载数字角标
        val emptyView = RemoteViews(context.packageName, android.R.layout.simple_list_item_1)
        val notification = android.app.Notification.Builder(context, "badge_channel")
            .setSmallIcon(R.mipmap.ic_launcher)
            .setCustomContentView(emptyView)
            .setNumber(count)
            .build()
        notificationManager.notify(1001, notification)
    }

    // OPPO适配
    private fun setOppoBadge(context: Context, count: Int) {
        try {
            val intent = Intent("com.oppo.unsettledevent")
            intent.putExtra("pakeageName", context.packageName)
            intent.putExtra("number", count)
            if (context.packageManager.resolveActivity(intent, 0) != null) {
                context.sendBroadcast(intent)
            } else {
                // 备用方案调用内部类
                val clazz = Class.forName("com.coloros.launcher.effect.EffectStatusManager")
                val method = clazz.getMethod("setBadgeNum", String::class.java, Int::class.java)
                method.invoke(clazz.newInstance(), context.packageName, count)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    // VIVO适配
    private fun setVivoBadge(context: Context, count: Int) {
        val launcherComponent = context.packageManager.getLaunchIntentForPackage(context.packageName)?.component ?: return
        val intent = Intent("com.vivo.notification.action.SET_BADGE")
        intent.putExtra("packageName", context.packageName)
        intent.putExtra("className", launcherComponent.className)
        intent.putExtra("badgenumber", count)
        context.sendBroadcast(intent)
    }

    // 原生点样式降级方案
    private fun setDotBadgeFallback(context: Context, count: Int) {
        val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = notificationManager.getNotificationChannel("dot_badge_channel")
                ?: notificationManager.createNotificationChannel(
                    android.app.NotificationChannel("dot_badge_channel", "Dot Badge", NotificationManager.IMPORTANCE_LOW)
                )
            val notification = android.app.Notification.Builder(context, "dot_badge_channel")
                .setSmallIcon(R.mipmap.ic_launcher)
                .setNumber(count)
                .build()
            notificationManager.notify(1002, notification)
        }
    }

    // 判断厂商
    private fun isMiui(): Boolean = Build.MANUFACTURER.lowercase().contains("xiaomi")
    private fun isHuawei(): Boolean = Build.MANUFACTURER.lowercase().contains("huawei")
    private fun isSamsung(): Boolean = Build.MANUFACTURER.lowercase().contains("samsung")
    private fun isOppo(): Boolean = Build.MANUFACTURER.lowercase().contains("oppo")
    private fun isVivo(): Boolean = Build.MANUFACTURER.lowercase().contains("vivo")
}

3. 权限配置

在AndroidManifest.xml中添加必要权限:

<!-- 华为角标权限 -->
<uses-permission android:name="com.huawei.android.launcher.permission.CHANGE_BADGE" />
<!-- 小米角标相关权限 -->
<uses-permission android:name="com.xiaomi.mipush.permission.MIPUSH_RECEIVE" />
<!-- OPPO角标权限 -->
<uses-permission android:name="com.coloros.mcs.permission.RECIEVE_MCS_MESSAGE" />

4. 使用方式

在通知数量变化时调用:

// 示例:更新未读消息角标
BadgeUtils.updateBadge(context, unreadMessageCount)

说明

  • 原生Android仅支持点样式,数字样式依赖厂商私有API,需注意厂商API可能随系统版本调整。
  • 用户可在系统设置中选择显示点样式或数字样式,应用只需实现两种逻辑,系统会根据用户设置展示对应样式。

内容的提问来源于stack exchange,提问作者Ahmed_Abdulrahman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 04:39:54