如何在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
相关产品推荐
相关产品推荐

