Android Oreo--Notifications


Android Oreo出來一段時(shí)間了,提供了很多新特性,比如通知欄樣式、自動填充、電池壽命優(yōu)化、自動調(diào)整TextView、XML中使用字體、可下載的字體和表情(有兩篇文章推薦Android Oreo可下載字體android開發(fā)排版指南)、自適應(yīng)圖標(biāo)、快捷方式固定、應(yīng)用程序的寬色域顏色、WebView功能增強(qiáng)、Java 8語言API和運(yùn)行時(shí)優(yōu)化,等等。
本文介紹Oreo 8.0對通知欄帶來了的變化以及用戶體驗(yàn)。

1,通知渠道

作為android開發(fā)者,肯定能很熟悉地寫出彈出通知欄的代碼:

var id = 1000
private fun showNotification() {
    var message = "message:${Math.random()}"
    var mNotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager//初始化要用到的系統(tǒng)服務(wù)
    var mBuilder = NotificationCompat.Builder(this)
    mBuilder.setContentTitle(getString(R.string.app_name))
            .setContentText("內(nèi)容")
//                .setContentIntent(getDefalutIntent(Notification.FLAG_AUTO_CANCEL))
            //              .setNumber(number)//顯示數(shù)量
            .setTicker("測試通知來啦")//通知首次出現(xiàn)在通知欄,帶上升動畫效果的
            .setWhen(System.currentTimeMillis())//通知產(chǎn)生的時(shí)間,會在通知信息里顯示
            .setPriority(Notification.PRIORITY_MAX)//設(shè)置該通知優(yōu)先級
            .setAutoCancel(true)//設(shè)置這個(gè)標(biāo)志當(dāng)用戶單擊面板就可以讓通知將自動取消
            .setOngoing(false)//ture,設(shè)置他為一個(gè)正在進(jìn)行的通知。他們通常是用來表示一個(gè)后臺任務(wù),用戶積極參與(如播放音樂)或以某種方式正在等待,因此占用設(shè)備(如一個(gè)文件下載,同步操作,主動網(wǎng)絡(luò)連接)
            //                .setDefaults(Notification.DEFAULT_VIBRATE)//向通知添加聲音、閃燈和振動效果的最簡單、最一致的方式是使用當(dāng)前的用戶默認(rèn)設(shè)置,使用defaults屬性,可以組合:
            //Notification.DEFAULT_ALL  Notification.DEFAULT_SOUND 添加聲音 // requires VIBRATE permission
            .setSmallIcon(R.mipmap.icon_anim_plane_1)
    mBuilder.setAutoCancel(true)//點(diǎn)擊后讓通知將消失
            .setContentTitle(getString(R.string.app_name))
            .setContentText(message)
            .setTicker(message)
    mNotificationManager.notify(id, mBuilder.build())
}

這是Oreo 8.0 (API 26)之前規(guī)范的寫法,但是這種寫法在Oreo 8.0手機(jī)上不能喚起通知欄,而且NotificationCompat.Builder(Context context)方法已經(jīng)被標(biāo)記為deprecated,取而代之的是NotificationCompat.Builder(Context context, String channelId)??梢钥吹?,構(gòu)造器添加了一個(gè)新的參數(shù)String channelId,這個(gè)是是渠道ID,每個(gè)通知都必須有一個(gè)渠道ID,其允許您為要顯示的每種通知類型創(chuàng)建用戶可自定義的渠道。用戶界面將通知渠道稱之為通知類別。

//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 }

}

上面代碼有點(diǎn)長,其實(shí)和API 26之前的寫法差別大概只有三點(diǎn):1,NotificationCompat.Builder使用了新的帶channelId參數(shù)的構(gòu)造器,分別在函數(shù)buildNotification()和函數(shù)buildSummary()中調(diào)用;2,根據(jù)channelId設(shè)置不同的smallIcon,參考方法getIconId();3,有了一個(gè)新的調(diào)用ChannelBuilder.ensureChannelsExist,它檢查不存在的channelId并創(chuàng)建相應(yīng)的渠道。

通知渠道提供了一些屬性:id(唯一標(biāo)識)、name(名字)、importance(重要性)、description(描述)、sound(聲音)、light(光)、vibrate(震動)、LockscreenVisibility(鎖屏可見性)、bypassDnd(免打擾)、showBadge(類似iOS的3DTouch)等等。

通過創(chuàng)建渠道組可以對渠道進(jìn)行分組,調(diào)用 setGroup()方法將某個(gè)渠道關(guān)聯(lián)到某個(gè)渠道組。需要注意的是,只能在將渠道提交給通知管理器NotificationManager之前修改渠道與渠道組之間的關(guān)聯(lián),這所以因?yàn)镹otificationManager調(diào)用createNotificationChannel()方法之后,再對NotificationChannel進(jìn)行的任何修改都將無效。代碼如下:

// 通知渠道組的id.
val group = "channel_group_01"
// 用戶可見的通知渠道組名稱.
var name = "name"
notificationManager.createNotificationChannelGroup(NotificationChannelGroup(group, name))

如果長按一條通知欄,會出現(xiàn)它的渠道名字和其它相關(guān)信息,如圖所示。



點(diǎn)擊“ALL CATEGORIES”按鈕進(jìn)入渠道相關(guān)屬性設(shè)置頁面。
使用新的API創(chuàng)建通知欄基本就這么多,貌似東西也不是很多哦。

2,通知標(biāo)志

Android 8.0中,長按app的啟動圖標(biāo)會顯示通知標(biāo)志,有點(diǎn)類似iOS的3DTouch。只有通知渠道設(shè)置屬性setShowBadge(true)(默認(rèn)是true)的時(shí)候,通知標(biāo)志才會起作用。通知標(biāo)志可反映某個(gè)應(yīng)用是否存在與其關(guān)聯(lián)、并且用戶尚未予以清除也未對其采取行動的通知。通知標(biāo)志也稱為通知點(diǎn)。


3,休眠

用戶可以將通知置于休眠狀態(tài),以便稍后重新顯示它。重新顯示時(shí)通知的重要程度與首次顯示時(shí)相同。應(yīng)用可以移除或更新已休眠的通知,但更新休眠的通知并不會使其重新顯示。操作方式是左滑或者右滑,這時(shí)會出現(xiàn)兩個(gè)按鈕:時(shí)鐘按鈕和設(shè)置按鈕,點(diǎn)擊時(shí)鐘按鈕。

4,通知超時(shí)

現(xiàn)在,使用 setTimeoutAfter() 創(chuàng)建通知時(shí)您可以設(shè)置超時(shí)。您可以使用此函數(shù)指定一個(gè)持續(xù)時(shí)間,超過該持續(xù)時(shí)間后,通知應(yīng)取消。如果需要,您可以在指定的超時(shí)持續(xù)時(shí)間之前取消通知。

5,通知設(shè)置

當(dāng)您使用Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCESIntent 從通知?jiǎng)?chuàng)建指向應(yīng)用通知設(shè)置的鏈接時(shí),您可以調(diào)用 setSettingsText() 來設(shè)置要顯示的文本。此系統(tǒng)可以提供以下 Extra 數(shù)據(jù)和 Intent,用于過濾應(yīng)用必須向用戶顯示的設(shè)置:EXTRA_CHANNEL_ID、NOTIFICATION_TAG 和 NOTIFICATION_ID。

6,通知清除

系統(tǒng)現(xiàn)在可區(qū)分通知是由用戶清除,還是由應(yīng)用移除。要查看清除通知的方式,您應(yīng)實(shí)現(xiàn) NotificationListenerService 類的新 onNotificationRemoved() 函數(shù)。

7,背景顏色

您現(xiàn)在可以設(shè)置和啟用通知的背景顏色。只能在用戶必須一眼就能看到的持續(xù)任務(wù)的通知中使用此功能。例如,您可以為與駕車路線或正在進(jìn)行的通話有關(guān)的通知設(shè)置背景顏色。您還可以使用 Notification.Builder.setColor() 設(shè)置所需的背景顏色。這樣做將允許您使用 Notification.Builder.setColorized() 啟用通知的背景顏色設(shè)置。

8,消息樣式

現(xiàn)在,使用 MessagingStyle 類的通知可在其折疊形式中顯示更多內(nèi)容。對于與消息有關(guān)的通知,您應(yīng)使用 MessagingStyle 類。您還可以使用新的 addHistoricMessage() 函數(shù),通過向與消息相關(guān)的通知添加歷史消息為會話提供上下文。

參考文獻(xiàn):

  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.itdecent.cn/p/92afa56aee05
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,168評論 25 708
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,635評論 19 139
  • 原文出處: http://www.androidchina.net/6174.html Notification在...
    木木00閱讀 12,547評論 3 32
  • 補(bǔ)第十六次作業(yè) 厲害的媽媽會養(yǎng)出什么樣的孩子 孩子的性格其實(shí)就是媽媽的體現(xiàn),強(qiáng)勢和太過自我的媽媽,孩子難免會自...
    墨行者閱讀 272評論 0 1
  • Java 1.5引入了自動裝箱和自動拆箱。這在有些時(shí)候很方便,但是要謹(jǐn)慎選擇。 它們的三個(gè)主要區(qū)別: 基本數(shù)據(jù)類型...
    DrunkPian0閱讀 282評論 0 0

友情鏈接更多精彩內(nèi)容