Android Oreo--Notifications

作者: 宛丘之上兮 | 来源:发表于2017-12-20 00:31 被阅读1196次

    Android Oreo出来一段时间了,提供了很多新特性,比如通知栏样式、自动填充、电池寿命优化、自动调整TextView、XML中使用字体、可下载的字体和表情(有两篇文章推荐Android Oreo可下载字体android开发排版指南)、自适应图标、快捷方式固定、应用程序的宽色域颜色、WebView功能增强、Java 8语言API和运行时优化,等等。
    本文介绍Oreo 8.0对通知栏带来了的变化以及用户体验。

    1,通知渠道

    作为android开发者,肯定能很熟悉地写出弹出通知栏的代码:

    var id = 1000
    private fun showNotification() {
        var message = "message:${Math.random()}"
        var mNotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager//初始化要用到的系统服务
        var mBuilder = NotificationCompat.Builder(this)
        mBuilder.setContentTitle(getString(R.string.app_name))
                .setContentText("内容")
    //                .setContentIntent(getDefalutIntent(Notification.FLAG_AUTO_CANCEL))
                //              .setNumber(number)//显示数量
                .setTicker("测试通知来啦")//通知首次出现在通知栏,带上升动画效果的
                .setWhen(System.currentTimeMillis())//通知产生的时间,会在通知信息里显示
                .setPriority(Notification.PRIORITY_MAX)//设置该通知优先级
                .setAutoCancel(true)//设置这个标志当用户单击面板就可以让通知将自动取消
                .setOngoing(false)//ture,设置他为一个正在进行的通知。他们通常是用来表示一个后台任务,用户积极参与(如播放音乐)或以某种方式正在等待,因此占用设备(如一个文件下载,同步操作,主动网络连接)
                //                .setDefaults(Notification.DEFAULT_VIBRATE)//向通知添加声音、闪灯和振动效果的最简单、最一致的方式是使用当前的用户默认设置,使用defaults属性,可以组合:
                //Notification.DEFAULT_ALL  Notification.DEFAULT_SOUND 添加声音 // requires VIBRATE permission
                .setSmallIcon(R.mipmap.icon_anim_plane_1)
        mBuilder.setAutoCancel(true)//点击后让通知将消失
                .setContentTitle(getString(R.string.app_name))
                .setContentText(message)
                .setTicker(message)
        mNotificationManager.notify(id, mBuilder.build())
    }
    

    这是Oreo 8.0 (API 26)之前规范的写法,但是这种写法在Oreo 8.0手机上不能唤起通知栏,而且NotificationCompat.Builder(Context context)方法已经被标记为deprecated,取而代之的是NotificationCompat.Builder(Context context, String channelId)。可以看到,构造器添加了一个新的参数String channelId,这个是是渠道ID,每个通知都必须有一个渠道ID,其允许您为要显示的每种通知类型创建用户可自定义的渠道。用户界面将通知渠道称之为通知类别。

    //NotificationBuilder.kt
    @TargetApi(Build.VERSION_CODES.O)
    class NotificationBuilder(
            private val context: Context,
            private val safeContext: Context = context.safeContext(),
            private val notificationManager: NotificationManagerCompat = NotificationManagerCompat.from(safeContext),
            private val channelBuilder: NotificationChannelBuilder = NotificationChannelBuilder(context, CHANNEL_IDS),
            private val random: Random = Random()
    ) {
    
        private var notificationId: Int = 62//by bindSharedPreference(context, KEY_NOTIFICATION_ID, 0)
    
        fun sendBundledNotification(message: Message) =
                with(notificationManager) {
                    channelBuilder.ensureChannelsExist(createChannel)
                    randomChannelId.also {
                        notify(notificationId++, buildNotification(message, it))
                        notify(SUMMARY_ID, buildSummary(message, it))
                    }
                }
    
        private val randomChannelId
            get() = CHANNEL_IDS[random.nextInt(CHANNEL_IDS.size)]
    
        private val createChannel: (channelId: String) -> NotificationChannel? = { channelId ->
            when (channelId) {
                IMPORTANT_CHANNEL_ID -> NotificationChannel(channelId,
                        context.getString(R.string.important_channel_name),
                        NotificationManager.IMPORTANCE_HIGH)
                        .apply {
                            description = context.getString(R.string.important_channel_description)
                        }
                NORMAL_CHANNEL_ID -> NotificationChannel(channelId,
                        context.getString(R.string.normal_channel_name),
                        NotificationManager.IMPORTANCE_DEFAULT)
                        .apply {
                            description = context.getString(R.string.normal_channel_description)
                        }
                LOW_CHANNEL_ID -> NotificationChannel(channelId,
                        context.getString(R.string.low_channel_name),
                        NotificationManager.IMPORTANCE_LOW)
                        .apply {
                            description = context.getString(R.string.low_channel_description)
                        }
                else -> null
            }
        }
    
        private fun buildNotification(message: Message, channelId: String): Notification =
                with(NotificationCompat.Builder(context, channelId)) {
                    message.apply {
                        setContentTitle(sender)
                        setContentText(text)
                        setWhen(timestamp?.toEpochMilli() ?: System.currentTimeMillis())
                    }
                    setSmallIcon(getIconId(channelId))
                    setShowWhen(true)
                    setGroup(GROUP_KEY)
                    build()
                }
    
        private fun getIconId(channelId: String) =
                when (channelId) {
                    IMPORTANT_CHANNEL_ID -> R.drawable.ic_important
                    LOW_CHANNEL_ID -> R.drawable.ic_low
                    else -> R.drawable.ic_message
                }
    
        private fun buildSummary(message: Message, channelId: String): Notification =
                with(NotificationCompat.Builder(context, channelId)) {
                    setContentTitle(SUMMARY_TITLE)
                    setContentText(SUMMARY_TEXT)
                    setWhen(message.timestamp?.toEpochMilli() ?: System.currentTimeMillis())
                    setSmallIcon(R.drawable.ic_message)
                    setShowWhen(true)
                    setGroup(GROUP_KEY)
                    setGroupSummary(true)
                    build()
                }
    
        companion object {
            private const val KEY_NOTIFICATION_ID = "KEY_NOTIFICATION_ID"
            private const val GROUP_KEY = "Messenger"
            private const val SUMMARY_ID = 0
            private const val SUMMARY_TITLE = "Nougat Messenger"
            private const val SUMMARY_TEXT = "You have unread messages"
            private const val IMPORTANT_CHANNEL_ID = "IMPORTANT_CHANNEL_ID"
            private const val NORMAL_CHANNEL_ID = "NORMAL_CHANNEL_ID"
            private const val LOW_CHANNEL_ID = "LOW_CHANNEL_ID"
            private val CHANNEL_IDS =
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                        listOf(IMPORTANT_CHANNEL_ID, NORMAL_CHANNEL_ID, LOW_CHANNEL_ID)
                    } else {
                        listOf(NORMAL_CHANNEL_ID)
                    }
        }
    }
    
    //NotificationChannelBuilder.kt
    class NotificationChannelBuilder(
            context: Context,
            private val channelIds: List<String>,
            private val notificationManager: NotificationManager =
            context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    ) {
    
        fun ensureChannelsExist(createChannel: (channelId: String) -> NotificationChannel?) =
                ifAtLeast(Build.VERSION_CODES.O) {
                    notificationManager.ensureChannelsExist(createChannel)
                }
    
        @TargetApi(Build.VERSION_CODES.O)
        private fun NotificationManager.ensureChannelsExist(createChannel: (channelId: String) -> NotificationChannel?) {
            channelIds
                    .filter { !notificationChannelIds().contains(it) }
                    .forEach {
                        createChannel(it)?.also {
                            notificationManager.createNotificationChannel(it)
                        }
                    }
        }
    
        @TargetApi(Build.VERSION_CODES.O)
        private fun NotificationManager.notificationChannelIds() =
                notificationChannels.map { it.id }
    
    }
    

    上面代码有点长,其实和API 26之前的写法差别大概只有三点:1,NotificationCompat.Builder使用了新的带channelId参数的构造器,分别在函数buildNotification()和函数buildSummary()中调用;2,根据channelId设置不同的smallIcon,参考方法getIconId();3,有了一个新的调用ChannelBuilder.ensureChannelsExist,它检查不存在的channelId并创建相应的渠道。

    通知渠道提供了一些属性:id(唯一标识)、name(名字)、importance(重要性)、description(描述)、sound(声音)、light(光)、vibrate(震动)、LockscreenVisibility(锁屏可见性)、bypassDnd(免打扰)、showBadge(类似iOS的3DTouch)等等。

    通过创建渠道组可以对渠道进行分组,调用 setGroup()方法将某个渠道关联到某个渠道组。需要注意的是,只能在将渠道提交给通知管理器NotificationManager之前修改渠道与渠道组之间的关联,这所以因为NotificationManager调用createNotificationChannel()方法之后,再对NotificationChannel进行的任何修改都将无效。代码如下:

    // 通知渠道组的id.
    val group = "channel_group_01"
    // 用户可见的通知渠道组名称.
    var name = "name"
    notificationManager.createNotificationChannelGroup(NotificationChannelGroup(group, name))
    

    如果长按一条通知栏,会出现它的渠道名字和其它相关信息,如图所示。



    点击“ALL CATEGORIES”按钮进入渠道相关属性设置页面。
    使用新的API创建通知栏基本就这么多,貌似东西也不是很多哦。

    2,通知标志

    Android 8.0中,长按app的启动图标会显示通知标志,有点类似iOS的3DTouch。只有通知渠道设置属性setShowBadge(true)(默认是true)的时候,通知标志才会起作用。通知标志可反映某个应用是否存在与其关联、并且用户尚未予以清除也未对其采取行动的通知。通知标志也称为通知点。


    3,休眠

    用户可以将通知置于休眠状态,以便稍后重新显示它。重新显示时通知的重要程度与首次显示时相同。应用可以移除或更新已休眠的通知,但更新休眠的通知并不会使其重新显示。操作方式是左滑或者右滑,这时会出现两个按钮:时钟按钮和设置按钮,点击时钟按钮。

    4,通知超时

    现在,使用 setTimeoutAfter() 创建通知时您可以设置超时。您可以使用此函数指定一个持续时间,超过该持续时间后,通知应取消。如果需要,您可以在指定的超时持续时间之前取消通知。

    5,通知设置

    当您使用Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCESIntent 从通知创建指向应用通知设置的链接时,您可以调用 setSettingsText() 来设置要显示的文本。此系统可以提供以下 Extra 数据和 Intent,用于过滤应用必须向用户显示的设置:EXTRA_CHANNEL_ID、NOTIFICATION_TAG 和 NOTIFICATION_ID。

    6,通知清除

    系统现在可区分通知是由用户清除,还是由应用移除。要查看清除通知的方式,您应实现 NotificationListenerService 类的新 onNotificationRemoved() 函数。

    7,背景颜色

    您现在可以设置和启用通知的背景颜色。只能在用户必须一眼就能看到的持续任务的通知中使用此功能。例如,您可以为与驾车路线或正在进行的通话有关的通知设置背景颜色。您还可以使用 Notification.Builder.setColor() 设置所需的背景颜色。这样做将允许您使用 Notification.Builder.setColorized() 启用通知的背景颜色设置。

    8,消息样式

    现在,使用 MessagingStyle 类的通知可在其折叠形式中显示更多内容。对于与消息有关的通知,您应使用 MessagingStyle 类。您还可以使用新的 addHistoricMessage() 函数,通过向与消息相关的通知添加历史消息为会话提供上下文。

    参考文献:

    1. https://blog.stylingandroid.com/oreo-notifications-channels-part-1/
    2. https://blog.stylingandroid.com/oreo-notifications-channels-part-2/
    3. https://developer.android.com/reference/android/app/NotificationChannel.html
    4. https://www.androidauthority.com/android-8-0-oreo-app-implementing-notification-channels-801097/
    5. https://developer.android.com/about/versions/oreo/index.html
    6. http://www.jianshu.com/p/92afa56aee05

    相关文章

      网友评论

        本文标题:Android Oreo--Notifications

        本文链接:https://www.haomeiwen.com/subject/sfriwxtx.html