本篇筆記主要是學習Android通知,主要包括通知渠道,通知分組,通知樣式,建立通知,通知關聯等部分。本篇筆記的學習資料主要是Android開發官網文檔的通知部分,能夠點擊鏈接直接進入到網站學習,下面是本篇筆記的內容。java
在以前的筆記中,已經學習了通知欄相關的大部分的內容,這篇筆記主要是對以前的筆記作一個總結和梳理,以及對以前筆記中沒有提到過的內容學習一下。android
經過以前的學習,已經明白了建立通知欄的流程主要包括如下步驟:微信
首先建立通知渠道(NotificationChannel
):markdown
雖然通知渠道是8.0版本及以上才須要加上的,可是如今Android版本已經更新到11了,所以對於通知渠道這裏默認是須要建立的,建立通知渠道須要設置如下屬性:session
建立完通知渠道以後經過createNotificationChannel
等方法註冊通知渠道。併發
接着建立通知app
通知也就是要顯示在通知欄的信息,建立通知須要設置如下信息:ide
smallIcon
的信息title
,content
信息style
屬性來知足不一樣的需求priority
屬性等,來保持和8.0擁有相同的重要性等。顯示通知工具
首先肯定一個通知Id,而後調用NotificationManagerCompat
的notify
方法將通知顯示出來。oop
下面的代碼演示瞭如何建立一個最基本的通知:
//發送一個基本的通知欄消息
private fun sendBasicNotification() {
//首先建立通知渠道
val channelId = "basicChannelId"
if (SdkVersionUtils.checkAndroidO()) { //這個工具類只是判斷當前的SDK版本是否大於等於8.0
val channelName = "基本的通知欄消息渠道"
val importance = NotificationManager.IMPORTANCE_HIGH
val channel = NotificationChannel(channelId, channelName, importance)
channel.description = "這個渠道下的消息都是爲了演示基本通知欄消息用的"
val channelManager = getSystemService(Context.NOTIFICATION_SERVICE)
as NotificationManager
channelManager.createNotificationChannel(channel)
}
//接下來建立通知
val builder = NotificationCompat.Builder(this, channelId)
builder.setSmallIcon(R.drawable.frank) //smallIcon是必須的
builder.setContentTitle("基本通知欄消息")
builder.setContentText("這是一條基本的通知欄消息")
val notificationInfo = builder.build()
//最後發送通知
val notificationId = 1000
NotificationManagerCompat.from(this).notify(notificationId, notificationInfo)
}
複製代碼
補充操做
在執行完上面的操做以後,咱們就已經能夠在通知欄顯示一條信息了,剩下的基本就對上面的步驟設置一些補充的操做,好比設置style
以適應特殊狀況下的樣式,設置PendingIntent
屬性來設置跳轉頁面等。
上面已經對若是發送一條通知欄消息有了明確的認識,總共三步便可顯示一條通知欄消息,而爲了可以適應開發中不一樣的需求,大多數狀況下都須要經過補充上面三步中的某一步。下面是經過補充/擴展渠道相關的信息。
經過查看NotificationChannel
部分的代碼,能夠發現渠道屬性這裏還有不少能夠設置的地方,以下演示渠道部分設置。
channel.enableLights(true)
設置這個屬性用於開啓當這個渠道的通道發佈時顯示通知燈。(實際在個人一加手機上嘗試並無效果)
lightColor
設置這個屬性用於當通知燈可用時的顏色(實際測試中發現也沒有效果)
enableVibration
設置這個屬性用於當這個渠道的通知發佈時是否開啓震動。(實際測試中沒有效果,在通知渠道設置頁面有一個選項可供用戶選擇是否開啓震動)
注意: 渠道的相關設置事實上主要受到用戶的影響,開發者並不能進行過多的控制,同時,就算開發者設置了部分屬性,最終也會以用戶設置的爲準。
讀取屬性的意義在於咱們能夠引導用戶進行某項設置,好比上面的設置震動沒有效果的時候,此時咱們就能夠引導用戶跳轉到設置頁面,讓用戶勾選容許震動,這樣當這個渠道的通知發佈的時候就會震動了。
經過如下三步讀取渠道屬性:
NotificationManager
,經過getSystemService(Context.NOTIFICATION_SERVICE)
來獲取NotificationChannel
,經過NotificationManager.getNotificationChannel(channelId)
來獲取shouldXXX
或者getXXX
相關方法來獲取。例以下面的代碼演示了獲取當前渠道發佈通知時是否容許震動:
val channelManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val readChannel = channelManager.getNotificationChannel(channelId)
val vibrate = readChannel.shouldVibrate()
Logs.e("是否開啓震動:$vibrate")
複製代碼
上面說了,有時候咱們設置的渠道屬性沒有效果,或者有時候咱們設置的屬性被用戶修改了,此時咱們能夠引導用戶跳轉到渠道信息設置頁面,讓用戶進行某項操做的設置。
跳轉渠道信息設置頁面主要經過Intent
完成,主要操做步驟以下:
val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
intent.putExtra(Settings.EXTRA_APP_PACKAGE, "應用包名")
intent.putExtra(Settings.EXTRA_CHANNEL_ID, "渠道ID")
startActivity(intent)
複製代碼
渠道分組的意義在於能夠對某些渠道進行分組,便於用戶管理。
可是經過以前的學習,我我的認爲渠道分組的意義並非很大,並且我我的認爲對於渠道的分組應該讓用戶本身去分組,而不是讓開發者分組好了讓用戶去操做。
要注意的是這裏只是我認爲意義不是很大。可是對於有多個帳戶須要管理的人來講這個渠道分組仍是頗有用的。
好比對於常用郵箱的人來講,不少人可能擁有多個郵箱帳號(好比我本身有一個163郵箱的,一個微軟outlook郵箱的,一個工做用的阿里雲郵箱的),此時我經過一個軟件,好比Foxmail來管理我這些郵箱帳號。每一個郵箱帳號都會有收到郵件的通知渠道,發送郵件成功的通知渠道,以及其它操做的通知渠道。這個時候,咱們發現,我這3個帳號有9個通知渠道,此時讓我去管理這些通知渠道就很麻煩,由於有時候很難分清哪一個通知渠道是屬於哪一個帳號下面的,這個時候渠道分組的優點就體現出來了,咱們針對每一個帳號建立一個渠道分組,每一個分組的名字就是這個郵箱帳號,每一個分組下面分別包含發送郵件,接收郵件,其它操做三個渠道。這樣,無論是我想操做某個渠道,仍是想操做某個帳號下全部的渠道,最起碼我不會不知道該去操做哪一個渠道了!
同時,渠道分組提供了能夠直接關閉或者開啓某一個渠道分組下全部渠道的通知,這樣,好比我這個163郵箱沒什麼用,常常收到一些垃圾郵件,那我就把這個渠道分組直接關閉掉,這樣163郵箱的通知就不會有了,可是不影響其它兩個渠道分組。
針對上面的案例,咱們經過如下代碼來實現:
//建立渠道分組
private fun createChannelGroup() {
if (SdkVersionUtils.checkAndroidO()) {
//分組id和名稱
//163郵箱的
val CHANNEL_GROUP_ID_163 = "channelGroupId163"
val CHANNEL_GROUO_NAME_163 = "163郵箱"
//outlook郵箱的
val CHANNEL_GROUP_ID_OUTLOOK = "channelGroupIdOutlook"
val CHANNEL_GROUO_NAME_OUTLOOK = "outlook郵箱"
//阿里雲郵箱的
val CHANNEL_GROUP_ID_ALI = "channelGroupIdAli"
val CHANNEL_GROUP_NAME_ALI = "阿里雲郵箱"
//建立渠道分組
val channelGroup163 =
NotificationChannelGroup(CHANNEL_GROUP_ID_163, CHANNEL_GROUO_NAME_163)
val channelGroupOutlook =
NotificationChannelGroup(CHANNEL_GROUP_ID_OUTLOOK, CHANNEL_GROUO_NAME_OUTLOOK)
val channelGroupAli =
NotificationChannelGroup(CHANNEL_GROUP_ID_ALI, CHANNEL_GROUP_NAME_ALI)
//通知管理器
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
//建立渠道分組
manager.createNotificationChannelGroup(channelGroup163)
manager.createNotificationChannelGroup(channelGroupOutlook)
manager.createNotificationChannelGroup(channelGroupAli)
//分別建立通知渠道,這裏爲了方便我直接循環建立
val channels = mutableListOf<NotificationChannel>()
for (i in 0 until 9) {
val channelId = when (i) {
0, 1, 2 -> "channelId_163_$i"
3, 4, 5 -> "channelId_outlook_$i"
else -> "channelId_ali_$i"
}
val channelName = when (i) {
0, 1, 2 -> "channelName_163_$i"
3, 4, 5 -> "channelName_outlook_$i"
else -> "channelName_ali_$i"
}
val channel = NotificationChannel(
channelId,
channelName,
NotificationManager.IMPORTANCE_DEFAULT
)
channel.group = when (i) {
0, 1, 2 -> CHANNEL_GROUP_ID_163
3, 4, 5 -> CHANNEL_GROUP_ID_OUTLOOK
else -> CHANNEL_GROUP_ID_ALI
}
channels.add(channel)
}
manager.createNotificationChannels(channels)
}
}
複製代碼
這樣,關於通知渠道的相關總結大概就是這些。整體來講,通知渠道的做用在於將具備相同行爲的通知集合在一塊兒,咱們經過通知渠道來統一管理某一個通知渠道下全部通知的行爲,好比響鈴,震動等。可是須要明確的是,只有用戶對通知的最終表現形式擁有決定權,開發者能夠設置某些屬性,可是並不必定會起做用。在這種狀況下讀取通知渠道信息就變得很重要,能夠經過讀取渠道信息引導用戶從新設置某些通知的表現形式。
在上面的發送一條普通的通知欄消息的代碼中,咱們首先是建立通知渠道,建立完通知渠道以後就開始建立通知,咱們使用NotificationCompat.Builder
來建立一條通知消息。
在上面咱們只是簡單地建立了一個通知欄消息,甚至連點擊以後的操做都沒有,其實對於通知消息最重要的就是這一塊,下面的內容就是對這一塊的內容進行填充,從而實現風格多樣的通知欄消息。
在以前瞭解了通知的渠道分組,和通知渠道,簡單來講,渠道的意義在於將具備相同功能,相同行爲的通知進行歸類,讓開發者和用戶可以對某一個渠道的具體行爲進行操做。渠道分組的意義則在於按照某一種方法對渠道進行分類總結,這樣作的目的在於當渠道相對較多以後用戶很難區分某一個渠道的功能,對渠道進行分組,有利於開發者和用戶對渠道進行管理。
通知分組在Android 7.0
就開始有了,比通知渠道出現的還要早。通知分組主要是爲了實現對通知UI的管理。上面說了通知渠道是對通知功能和行爲進行分類,這是兩者的差異。仍然使用上面提到的郵箱舉例:在一個郵箱帳號下可能存在多個聯繫人,同一個時間段,同一個聯繫人可能會給咱們發送多條郵件,在沒有通知分組的狀況下,每一條通知都以獨立的形式顯示在通知欄中,通知多了以後很難進行管理。而有了通知分組以後,咱們就能夠將同一個聯繫人的郵件歸類到同一個分組之下,這樣用戶就能夠在通知欄中對某一個聯繫人的通知進行管理(能夠打開或者合併)。
下面的代碼演示了建立通知分組的過程:
val builder = NotificationCompat.Builder(this,CHANNEL_ID_VIDEO)
builder.setSmallIcon(R.drawable.frank)
builder.setContentTitle("通知分組測試")
builder.setContentText("這是通知分組測試")
builder.setLargeIcon(BitmapFactory.decodeResource(resources,R.drawable.frank))
builder.setGroup(NOTIFICATION_GROUP_KEY_VIDEO)
複製代碼
能夠看到,上面就是添加了一條普通的通知消息,惟一有區別的就是在最後加入了setGroup()
屬性,顯示這條通知消息也和普通的通知消息沒有什麼不一樣。
通知組摘要說明了當前通知組下面的通知的一些簡略信息,在Android7.0
以及更高的版本上,系統會使用每條通知中的文本摘要,自動建立通知組的文本摘要,用戶能夠展開此通知來查看每條單獨的通知。而對於低於Android7.0
版本的系統,爲了可以表現一致,則須要另外建立一條通知來充當通知組摘要。在這種狀況下,系統會隱藏這個通知組下的全部通知,而僅僅顯示這個通知組的信息。在這樣的狀況下,這個通知組的摘要就須要包含其它通知的片斷,供用戶點擊來打開應用。
val NOTIFICATION_GROUP_KEY_VIDEO = "notificationGroupIdVideo"//通知分組的惟一標識
val SUMMARY_GROUP_ID = 0 //通知組摘要ID 也就是顯示這個通知組的時候的ID
private fun createNotificationGroup() {
//第一條消息
val newMessage1 = NotificationCompat.Builder(this, "channelId_163_0").apply {
setSmallIcon(R.drawable.frank)
setContentTitle("第一條消息")
setContentText("這裏是第一條消息")
setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.frank))
setGroup(NOTIFICATION_GROUP_KEY_VIDEO)
}.build()
//第二條消息
val newMessage2 = NotificationCompat.Builder(this, "channelId_163_0").apply {
setSmallIcon(R.drawable.frank)
setContentTitle("第二條新消息")
setContentText("這裏第二條新消息")
setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.frank))
setGroup(NOTIFICATION_GROUP_KEY_VIDEO)
}.build()
//分組摘要
val summaryNotification = NotificationCompat.Builder(this, "channelId_163_0").apply {
setContentTitle("通知分組測試")
setContentText("兩條新消息")
setSmallIcon(R.drawable.frank)
setStyle(
NotificationCompat.InboxStyle()
.setBigContentTitle("兩條新消息")
.setSummaryText("摘要內容")
)
.setGroup(NOTIFICATION_GROUP_KEY_VIDEO)
.setGroupSummary(true)
}.build()
NotificationManagerCompat.from(this).apply {
notify(100, newMessage1)
notify(101, newMessage2)
notify(SUMMARY_GROUP_ID, summaryNotification)
}
}
複製代碼
運行上面的程序,最終會在通知欄顯示這兩條消息,可是這兩條消息都是處於摺疊狀態,點擊摘要區域或者下滑操做能夠打開通知的詳細信息。另外須要注意的是,通知分組的顯示效果和當前通知所在渠道的重要性是有關係的,若是將當前渠道的重要性設置爲高,則最新的消息始終是打開狀態,其他的消息會摺疊在摘要通知下面(在個人一加手機上是如此)。
以前咱們已經瞭解瞭如何顯示一個普通的通知欄消息,可是咱們並無設置點擊通知欄消息的時候執行的操做。通常狀況下咱們都會在點擊通知欄後執行一些操做,好比打開Activity,或者發送一個廣播之類的。下面的代碼演示瞭如何在點擊通知欄以後跳轉到一個Activity頁面:
//測試跳轉一個普通的Activity
private fun toActivity() {
//建立渠道
createToActivityChannel()
//構建須要跳轉到的Activity
val intent = Intent(this, NotificationChannelTestActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
val pendingIntent =
PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
//建立通知消息
val notification = NotificationCompat.Builder(this, mToActivityChannel).apply {
setSmallIcon(R.drawable.frank)
setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.frank))
setContentTitle("跳轉Activity測試")
setContentText("用於測試是否可以跳轉到Activity")
setAutoCancel(true)
setContentIntent(pendingIntent)
priority = NotificationCompat.PRIORITY_DEFAULT
}.build()
//發送通知
NotificationManagerCompat.from(this).notify(100, notification)
}
複製代碼
使用上面的代碼就能夠在通知欄顯示一條通知消息,同時,點擊這條消息則能夠跳轉到咱們設置的NotificationChannelTestActivity
這個頁面。
對於通常的頁面跳轉使用上面的方式指定是沒有問題的,可是除了上面的這種方式,Android還爲咱們提供了另外的兩種方式來應對不一樣的狀況。
這類的Activity是指咱們在正常使用APP的時候就會使用到的頁面,所以,當用戶從通知跳轉到這類Activity的時候,新的任務應該包含完整的返回堆棧,以便用戶能夠按返回鍵返回到以前的頁面。
好比咱們常用的通訊APP,有時候當咱們收到消息推送的時候極可能APP進程已經被殺死了,那麼此時在點擊通知欄的時候,雖然咱們仍然可以跳轉到正常的頁面,可是點擊返回鍵的時候此時就沒有正常的任務棧了,沒法返回到上個頁面,而是直接返回到桌面了。這個時候,咱們就能夠經過TaskStackBuilder
設置PendingIntent
,這樣就能夠建立新的返回堆棧,以便用戶能夠執行正常的返回流程。
經過TaskStackBuilder
建立返回堆棧的流程以下:
TestTaskStackBuilderActivity1
和TestTaskStackBuilderActivity2
,正常的流程是SummaryActivity
-> TestTaskStackBuilderActivity1
-> TestTaskStackBuilderActivity2
,而點擊通知欄消息則會直接進入到TestTaskStackBuilderActivity2
這個頁面,爲了可以在點擊通知欄消息的時候也可以有正常的返回流程,因此首先須要在Manifest
文件中註冊每個Activity
的parentActivityName
屬性:<activity
android:name=".notification.summary.TestTaskStackBuilderActivity2"
android:parentActivityName=".notification.summary.TestTaskStackBuilderActivity1"></activity>
<activity
android:name=".notification.summary.TestTaskStackBuilderActivity1"
android:parentActivityName=".notification.summary.SummaryActivity" />
複製代碼
TaskStackBuilder
來建立PendingIntent
://使用TaskStackBuilder構建PendingIntent
private fun toActivityWithTaskStackBuilder() {
//渠道信息
val channelId = "testTaskStackBuilder"
val channelName = "測試返回任務棧"
val channelDescription = "用於測試返回任務棧的通知欄消息渠道"
//建立渠道
if (SdkVersionUtils.checkAndroidO()) {
val channel =
NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT)
channel.description = channelDescription
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(channel)
}
//通知信息
val notificationId = 100
//構建要跳轉的頁面
val intent = Intent(this, TestTaskStackBuilderActivity2::class.java)
val taskBuilder = TaskStackBuilder.create(this).apply {
this.addParentStack(TestTaskStackBuilderActivity2::class.java)
this.addNextIntent(intent)
}
val pendingIntent = taskBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
//構建通知
val builder = NotificationCompat.Builder(this, channelId).apply {
setSmallIcon(R.drawable.frank)
setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.frank))
setContentTitle("測試返回棧")
setContentText("測試任務返回棧的消息")
setContentIntent(pendingIntent)
priority = NotificationCompat.PRIORITY_DEFAULT
setAutoCancel(true)
}
Handler().postDelayed({
//發送通知
NotificationManagerCompat.from(this).notify(notificationId, builder.build())
}, 5000)
}
複製代碼
經過上面的代碼構建的PendingIntent
,在APP處於後臺的時候也可以準確的跳轉到對應的頁面,返回時也可以按照正常的返回邏輯進行跳轉。可是有一個問題在於當APP處於前臺的時候,這個時候點擊通知欄消息,此時跳轉到指定的頁面,退出的時候這個頁面以前的頁面也會被重建。好比此時咱們已經打開並處於SummaryActivity
這個頁面了,可是經過點擊通知欄消息跳轉到TestTaskStackBuilderActivity2
頁面以後,點擊返回鍵返回到SummaryActivity
的時候,這個頁面又被重建了。
普通狀況下這樣作不會有什麼問題,可是若是相似於SummaryActivity
這樣的頁面操做邏輯不少,那麼就會有問題了。好比在MainActivity
咱們可能會請求後臺是否有新版本更新,有的話就彈出一個Dialog
,原本應該是每次打開APP獲取一次,可是使用這樣的通知欄消息的時候,返回到MainActivity
的時候就會致使MainActivity
頁面被重建,又一次彈出這個Dialog
,就會形成很差的體驗。所以仍是要根據不一樣的狀況選擇不一樣的方式。
除了上面的須要返回棧的狀況外,有時候咱們也會遇到某個頁面只有在點擊通知欄以後纔會跳轉過去,正常使用APP是不會跳轉到這樣的頁面的這樣的狀況。對於這樣的Activity,咱們能夠直接使用PeindingIntent.getActivity
來構建,同時配合在Manifest
文件中設置的屬性,就能夠達到相應的效果,以下代碼演示瞭如何建立這樣的PendingIntent
:
Manifest
文件中設置這個Activity的launchMode
爲singleTask
,同時設置taskAffinity
爲空,用來保證普通狀況下不會進入到這個頁面。同時設置excludeFromRecents
屬性爲true
,用來保證這個頁面不會進入到最近任務列表中。(這裏並不能保證這個頁面在任何狀況下都不會進入到最近任務列表,若是當前正處於這個頁面,那麼按下最近任務按鈕,仍然會看這個頁面)<activity android:name=".notification.summary.SpecialActivity"
android:launchMode="singleTask"
android:taskAffinity=""
android:excludeFromRecents="true"
></activity>
複製代碼
//跳轉到特殊的Activity
private fun toSpecialActivity() {
//渠道信息
val channelId = "testSpecialActivity"
val channelName = "測試跳轉到特殊的Activity"
val channelDescription = "用於測試跳轉到特殊Activity的通知欄消息渠道"
//建立渠道
if (SdkVersionUtils.checkAndroidO()) {
val channel =
NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT)
channel.description = channelDescription
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(channel)
}
//通知信息
val notificationId = 100
//構建要跳轉的頁面
val intent = Intent(this, SpecialActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
val pendingIntent =
PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
//構建通知
val builder = NotificationCompat.Builder(this, channelId).apply {
setSmallIcon(R.drawable.frank)
setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.frank))
setContentTitle("測試跳轉特殊Activity")
setContentText("只有經過點擊通知欄消息才能跳轉到的Activity")
setContentIntent(pendingIntent)
priority = NotificationCompat.PRIORITY_DEFAULT
setAutoCancel(true)
}
//發送通知
NotificationManagerCompat.from(this).notify(notificationId, builder.build())
}
複製代碼
在上面咱們已經學習了關於通知的一些知識,經過上面這些知識其實咱們已經基本可以知足平常的需求,可是在上面的基礎上,Android也爲咱們提供了更多的通知欄樣式,從而讓咱們的通知可以更加個性化。展開式通知就是爲了達到這樣的效果,咱們能夠在設置通知的時候設置不一樣的Style
,來達到本身的需求。
若是須要在通知中顯示圖片,則可使用BigPictureStyle
:
//顯示大圖片的通知
private fun createBigPictureNotification() {
val channelUtils = ChannelUtils.getInstance(this);
//建立通道
channelUtils.createDefaultChannel()
//建立通知
val builder = NotificationCompat.Builder(this, channelUtils.defaultChannelId)
builder.setSmallIcon(R.drawable.frank)
builder.setContentTitle("大圖通知")
builder.setContentText("大圖通知")
builder.setStyle(
NotificationCompat.BigPictureStyle()
.bigLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.frank))
.bigPicture(BitmapFactory.decodeResource(resources, R.drawable.frank))
.setSummaryText("二級標題")
)
builder.priority = NotificationCompat.PRIORITY_DEFAULT
builder.setAutoCancel(true)
//顯示通知
NotificationManagerCompat.from(this).notify(100, builder.build())
}
複製代碼
上面的代碼中,經過指定建立通知時的Style
屬性就能夠建立一個大圖通知,在通知欄打開的時候,通知信息就是咱們指定的這個圖片。
若是想要在通知中顯示大段的文本信息,則能夠經過指定Style
屬性爲BigTextStyle
屬性來設置:
private fun createBigTextNotification() {
//建立一個渠道
createTestStyleChannel()
//通知相關的信息
val title = "大段文本通知"
val content = "測試大段文本的通知"
//建立大段文本通知的style
val style = NotificationCompat.BigTextStyle().run {
this.bigText("這裏是大段通知文本這裏是大段通知文本這裏是大段通知文本這裏是大段通知文本這裏是大段通知文本")
this.setSummaryText("用於測試大段文本通知的style")
}
val notificationUtils = NotificationUtils.getInstance(this)
//建立通知實體
val builder = notificationUtils
.createNotification(
channelId,
R.drawable.frank,
title,
content,
style,
null,
testStyleNotificationGroupKey
)
//發送通知
notificationUtils.sendNotification(builder.build())
//建立併發送通知分組
createTestStyleNotificationGroup()
}
複製代碼
經過上面的代碼就能夠成功地建立出一個包含大段文本的通知,默認狀況下會顯示content
設置的內容,當通知展開後就會顯示設置的bigText
的內容。上面主要的代碼是建立出style
,使用到的createTestStyleChannel()
和createTestStyleNotificationGroup()
則是兩個工具類,會在本篇筆記學習完Style
以後貼出來。
若是在一條通知中想要顯示多個通知內容,好比收到多條郵件,每條信息展現一條通知簡介,咱們則可使用InboxStyle
這樣的樣式來設置,這裏須要注意的是InboxStyle
和通知分組的區別:通知分組下的每一條通知都有本身單獨的行爲屬性,好比每一條單獨的通知均可以設置樣式,設置跳轉頁面,可是InboxStyle
只是某一條通知的樣式不一樣,本質上只是一條通知。通知分組理論上也能夠實現InboxStyle
的效果,可是對於剛纔的例子,咱們只想展現郵件簡介而沒有其它特殊內容,此時使用通知分組則有些殺雞用牛刀的感受。
使用下面的代碼能夠建立InboxStyle
類型的通知:
//建立收件箱樣式的通知
private fun createInboxStyleNotification() {
//建立渠道
createTestStyleChannel()
//通知內容
val title = "郵件"
val content = "收到新郵件"
val style = NotificationCompat.InboxStyle().run {
this.addLine("第一條簡介")
this.addLine("第二條簡介")
this.addLine("第三條簡介")
}
//建立消息
val utils = NotificationUtils.getInstance(this)
val builder = utils.createNotification(
testStyleChannelId,
R.drawable.frank,
title,
content,
style,
null,
testStyleNotificationGroupKey
)
//發送消息
utils.sendNotification(builder.build())
//建立併發送通知分組
createTestStyleNotificationGroup()
}
複製代碼
使用上面的代碼則能夠在通知欄中顯示收件箱樣式的通知消息,默認狀況下顯示的是contentText
配置的內容,點擊展開以後顯示的就是咱們添加的這三條簡介內容。
使用MessagingStyle
能夠顯示任意人數之間依序發送的消息,對於即時通信應用來講這種方式是很是友好的,以下代碼能夠在通知欄建立對話通知:
//在通知中顯示對話
private fun createMessagingStyleNotification() {
//建立渠道
createTestStyleChannel()
//通知消息實體
val title = "聯繫人"
val content = "聯繫人發來新消息"
//建立發送消息用戶的信息
val user = Person.Builder().run {
this.setIcon(IconCompat.createWithResource(this@SummaryActivity, R.drawable.ic_icon1))
this.setName("Bob")
}
//建立style
val style = NotificationCompat.MessagingStyle(user.build()).run {
//添加消息
this.addMessage("Hello World",System.currentTimeMillis() - 1000 * 60 * 60 * 24,user.build())
this.addMessage("Hello World",System.currentTimeMillis() - 1000 * 60 * 60 * 24,user.build())
this.addMessage(NotificationCompat.MessagingStyle.Message("Thank you",System.currentTimeMillis(),user.build()))
}
//建立消息
val utils = NotificationUtils.getInstance(this)
val builder = utils.createNotification(testStyleChannelId,R.drawable.frank,title,content,style,null,testStyleNotificationGroupKey)
//發送消息
utils.sendNotification(builder.build())
//建立併發送通知分組消息
createTestStyleNotificationGroup()
}
複製代碼
使用上面的代碼會在通知欄顯示對話消息,須要注意的是,顯示對話消息的時候,咱們設置的contentTitle
和contentText
內容將不會顯示,同時在通知欄默認顯示兩條消息,若是消息多餘兩條則會摺疊起來,點擊展開以後則會顯示所有對話消息。
當咱們的應用在播放視頻或者音樂的時候,咱們可能會須要在通知欄顯示當前播放的曲目信息,此時就須要使用媒體控件來建立通知消息。
在使用媒體控件建立通知欄消息的時候,能夠經過調用addAction
來添加按鈕,最多可以添加5個,用於執行不一樣的操做。同時能夠調用setLargeIcon()
來設置專輯封面。同時,咱們還能夠經過setShowActionsInCompactView()
來設置通知收起以後仍然要顯示的按鈕。
同時,若是通知表示媒體會話,則還能夠調用setMediaSession()
在通知上附加MediaSession.Token
,這樣,系統就會將其識別爲表示媒體會話的通知並相應的作出響應(例如在鎖屏中顯示專輯封面)
經過以下代碼建立媒體控件通知:
//建立媒體控件通知
private fun createMediaStyleNotification() {
//建立渠道
createTestStyleChannel()
//建立消息實體
//五個按鈕對應的操做
val operateAction = TestMediaStyleBroadcastReceiver::class.java.name
//上一首
val preIntent = Intent(operateAction)
//preIntent.action = operateAction
preIntent.putExtra(operateKey, "上一首")
val prePendingIntent =
PendingIntent.getBroadcast(this, 0, preIntent, PendingIntent.FLAG_UPDATE_CURRENT)
//下一首
val nextIntent = Intent()
nextIntent.action = operateAction
nextIntent.putExtra(operateKey, "下一首")
val nextPendingIntent =
PendingIntent.getBroadcast(this, 1, nextIntent, PendingIntent.FLAG_UPDATE_CURRENT)
//播放或者暫停
val stateIntent = Intent()
stateIntent.action = operateAction
stateIntent.putExtra(operateKey, "播放/暫停")
val statePendingIntent =
PendingIntent.getBroadcast(this, 2, stateIntent, PendingIntent.FLAG_UPDATE_CURRENT)
//收藏
val collectionIntent = Intent()
collectionIntent.action = operateAction
collectionIntent.putExtra(operateKey, "收藏")
val collectionPendingIntent =
PendingIntent.getBroadcast(this, 3, collectionIntent, PendingIntent.FLAG_UPDATE_CURRENT)
//喜歡
val likeIntent = Intent()
likeIntent.action = operateAction
likeIntent.putExtra(operateKey, "喜歡")
val likePendingIntent =
PendingIntent.getBroadcast(this, 4, likeIntent, PendingIntent.FLAG_UPDATE_CURRENT)
//建立消息實體
val utils = NotificationUtils.getInstance(this)
val style = androidx.media.app.NotificationCompat.MediaStyle().run {
this.setShowActionsInCompactView(0, 1, 3)
}
val builder = utils.createNotification(
testStyleChannelId,
R.drawable.frank,
R.drawable.frank,
"My Music",
"個人音樂",
null,
style,
true,
NotificationCompat.PRIORITY_DEFAULT,
null
)
builder.addAction(R.mipmap.ic_back_black, "pre", prePendingIntent)
builder.addAction(R.mipmap.ic_back_white, "state", statePendingIntent)
builder.addAction(R.mipmap.ic_launcher, "next", nextPendingIntent)
builder.addAction(R.mipmap.ic_search_white, "collection", collectionPendingIntent)
builder.addAction(R.mipmap.ic_bg_layout_title, "like", likePendingIntent)
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
builder.color = Color.parseColor("#336699")
//發送消息
utils.sendNotification(1000, builder.build())
}
複製代碼
使用上面的代碼,咱們就能夠建立出一個媒體通知樣式的通知欄,包含5個操做按鈕。
須要注意的是,若是項目使用的是androidx
,那麼在使用MediaStyle
的時候可能出現找不到這個類的問題,此時須要導入androidx
的media
庫,包括以下部分:
"androidx.media2:media2-session:1.0.3",
"androidx.media2:media2-widget:1.0.3",
"androidx.media2:media2-player:1.0.3"
複製代碼
版本號能夠在官方文檔中查詢最新版。
上面建立通知渠道和通知的工具類代碼以下:
public void createChannel(
String channelId,
String channelName,
int importance,
String description,
String groupId
) {
if (moreThan8()) {
NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
channel.setDescription(description);
if (!TextUtils.isEmpty(groupId))
channel.setGroup(groupId);
NotificationManager manager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
manager.createNotificationChannel(channel);
}
}
複製代碼
public NotificationCompat.Builder createNotification(
String channelId,
int smallImageRes,
Integer largeImageRes,
String title,
String content,
PendingIntent pendingIntent,
NotificationCompat.Style style,
boolean cancel,
int priority,
String groupKey
) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId);
//小圖標
builder.setSmallIcon(smallImageRes);
//大圖標,能夠不設置
if (largeImageRes != null)
builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), largeImageRes));
//標題
builder.setContentTitle(title);
//內容
builder.setContentText(content);
//點擊以後執行的操做,跳轉頁面或者發送通知等
if (pendingIntent != null)
builder.setContentIntent(pendingIntent);
//設置樣式
if (style != null)
builder.setStyle(style);
//是否點擊後能夠取消
builder.setAutoCancel(cancel);
//重要性,這裏須要和渠道中的重要性保持一致
builder.setPriority(priority);
//不容許添加默認操做按鈕
builder.setAllowSystemGeneratedContextualActions(false);
if (!TextUtils.isEmpty(groupKey))
builder.setGroup(groupKey);
return builder;
}
複製代碼
在上面咱們已經將通知這裏學習的差很少了,對於不一樣的需求上面的學習過程基本都涉及到了,可是還有一些比較特殊的沒有涉及到,以下是一些比較特殊的設置。
其實在上面的MediaStyle
的時候咱們已經添加了5個操做按鈕,可是對於普通的通知,最多隻能添加3個操做按鈕,添加的方式和上面的操做是同樣的,都是經過給Intent
設置action
來啓動一個BroadcastReceiver
或者Activity
:
//添加操做按鈕
private fun createHaveActionsNotification() {
//建立渠道
createTestStyleChannel()
//建立通知實體
val title = "操做按鈕"
val content = "包含操做按鈕的通知"
//兩個操做按鈕
val intent1 = Intent(TestMediaStyleBroadcastReceiver::class.java.name)
val action1 =
PendingIntent.getBroadcast(this, 10, intent1, PendingIntent.FLAG_UPDATE_CURRENT)
val intent2 = Intent(this, MessagingStyleActivity::class.java)
val action2 =
PendingIntent.getActivity(this, 11, intent2, PendingIntent.FLAG_UPDATE_CURRENT)
//建立通知工具類
val utils = NotificationUtils.getInstance(this)
val builder = utils
.createNotification(testStyleChannelId, R.drawable.frank, title, content)
//將操做按鈕添加到通知上
builder.addAction(R.drawable.ic_icon1, "已讀", action1)
builder.addAction(R.drawable.ic_icon4, "查看詳情", action2)
//顯示通知
utils.sendNotification(builder.build())
}
複製代碼
在上面的代碼中,建立了兩個操做按鈕,一個點擊以後會發出一個廣播,一個點擊以後會跳轉到指定的Activity
頁面。
在Android7.0
及以上的版本中引入了直接回復操做,用戶能夠在通知欄中直接添加要回復的內容而沒必要打開Activity
,建立直接回復的流程以下:
//添加直接回復操做的通知
private val REPLY_TEXT_KEY = "replyTextKey"
//回覆的對象
private val REPLY_USER_ID = "replyUserId"
//通知id
private val REPLAY_NOTIFICATION_ID = 5000
private fun createHaveReplyNotification() {
//建立通知渠道
createTestStyleChannel()
//通知實體
val title = "直接回復操做的通知"
val content = "點擊下面的回覆按鈕能夠直接回覆信息"
val utils = NotificationUtils.getInstance(this)
val builder = utils.createNotification(testStyleChannelId, R.drawable.frank, title, content)
//用於直接回復的`RemoteInput.Builder`
val remoteInput = RemoteInput.Builder(REPLY_TEXT_KEY).run {
setLabel("請輸入要回復的內容")
build()
}
//爲回覆建立PendingIntent
val replyRequestCode = 200
val intent = Intent(TestMediaStyleBroadcastReceiver::class.java.name)
intent.putExtra(REPLY_USER_ID, replyRequestCode)
val replyPendingIntent =
PendingIntent.getBroadcast(
this,
replyRequestCode,
intent,
PendingIntent.FLAG_UPDATE_CURRENT
)
//建立用於回覆的action 並綁定remoteInput
val replyAction =
NotificationCompat.Action.Builder(R.drawable.ic_icon6, "當即回覆", replyPendingIntent)
.addRemoteInput(remoteInput)
.build()
//將action綁定到通知上
builder.addAction(replyAction)
//發送通知
utils.sendNotification(REPLAY_NOTIFICATION_ID,builder.build())
}
複製代碼
經過上面的代碼就能夠在狀態欄建立一個可以直接回復消息的通知,在通知中會有一個當即回覆
的按鈕,點擊這個按鈕就能夠調出輸入法,而後能夠輸入要回復的內容了。
不少時候咱們不只須要讓用戶可以直接回復,咱們還須要獲取到用戶回覆的內容,經過如下方式能夠獲取到用戶回覆的內容:
在上面的代碼中,咱們對回覆按鈕綁定的是一個BroadcastReceiver
,那麼在這個receiver
中就能夠獲取到用戶輸入的內容:
//獲取用戶回覆的內容
val replayIntent = RemoteInput.getResultsFromIntent(intent)
replayIntent?.let {
Log.e("TAG","用戶回覆的內容:${it.getCharSequence(REPLY_TEXT_KEY)}")
}
複製代碼
在成功獲取到用戶輸入的內容並執行完操做以後,咱們可能傾向於繼續對這條通知作一些操做,或者隱藏這條通知,經過以下方式修改通知:
//收到用戶回覆的內容後更新通知
createTestStyleChannel()
val utils = NotificationUtils.getInstance(context)
val builder = utils.createNotification(
testStyleChannelId,
R.drawable.frank,
"直接回復操做的通知",
"回覆成功"
)
utils.sendNotification(REPLY_NOTIFICATION_ID, builder.build())
//等待3秒鐘刪除掉這條通知
Handler().postDelayed({
utils.deleteNotification(REPLY_NOTIFICATION_ID)
},3000)
複製代碼
在上面的代碼中咱們在收到回覆內容並處理以後更新了這條通知,而後在等待3秒以後關閉了這條通知。
有時候咱們須要提醒用戶一些進度信息,好比當咱們在執行下載等任務的時候,此時就能夠在通知欄中添加進度條。在通知欄中的進度條分爲兩種,一種是可以肯定當前進度的,另一種是不能肯定當前進度,也就是不肯定結束時間的進度條,多於這兩種進度條差異並不大,以下分別建立一個肯定結束時間的進度條和一個不肯定結束時間的進度條:
//建立一個肯定結束時間的進度條
private val mNotificationUtils by lazy {
NotificationUtils.getInstance(this)
}
private val mWithFinishProgressBuilder by lazy {
//建立渠道
createTestStyleChannel()
mNotificationUtils.createNotification(
testStyleChannelId,
R.drawable.frank,
"進度條通知",
"包含進度條的通知"
).run {
setOnlyAlertOnce(true)
}
}
private var mProgress = 0;
private val FINISH_PROGRESS_NOTIFICATION_ID = 4000
private val mFinishNotificationHandler by lazy {
Handler()
}
private fun createWithFinishNotification() {
mWithFinishProgressBuilder.setProgress(100, mProgress, false)
mNotificationUtils.sendNotification(
FINISH_PROGRESS_NOTIFICATION_ID,
mWithFinishProgressBuilder.build()
)
if (mProgress >= 100) {
mFinishNotificationHandler.removeCallbacksAndMessages(null)
mNotificationUtils.deleteNotification(FINISH_PROGRESS_NOTIFICATION_ID)
} else {
mProgress += 10
mFinishNotificationHandler.postDelayed({
createWithFinishNotification()
}, 1000)
}
}
複製代碼
經過上面的代碼咱們就能夠建立出一個肯定結束時間的進度條,上面使用Handler
模擬進度條每次增長10。另外,因爲這條通知渠道的重要新爲IMPORTANCE_DEFAULT
,因此設置setOnlyAlertOnce(true)
能夠保證只會響鈴一次,也能夠設置IMPORTANCE_LOW
關閉響鈴。
不肯定結束時間的進度條和上面建立的肯定結束時間的進度條差異不大,只是在NotificationCompat.Builder.setProgress(0,0,true)
傳遞這樣的參數就能夠了,以下所示:
//建立不肯定結束時間的進度條通知
private val INDETERMINATE_NOTIFICATION_ID = 40001
private val mIndeterminateBuilder by lazy {
createTestStyleChannel()
mNotificationUtils.createNotification(
testStyleChannelId,
R.drawable.frank,
"進度條通知",
"不肯定結束時間的進度條"
).run {
setOnlyAlertOnce(true)
}
}
private var mTime = 0;
private fun createIndeterminateNotification() {
mIndeterminateBuilder.setProgress(0, 0, true)
mNotificationUtils.sendNotification(
INDETERMINATE_NOTIFICATION_ID,
mIndeterminateBuilder.build()
)
if (mTime >= 10 * 1000) {
mFinishNotificationHandler.removeCallbacksAndMessages(null)
mNotificationUtils.deleteNotification(INDETERMINATE_NOTIFICATION_ID)
} else {
mFinishNotificationHandler.postDelayed(
{
mTime += 1000
createIndeterminateNotification()
}, 1000
)
}
}
複製代碼
經過設置setVisibility()
屬性來控制通知在鎖定屏幕上的顯示級別,這個屬性取值範圍以下:
VISIBILITY_PUBLIC
:顯示完整的通知內容VISIBILITY_SECRET
: 不在鎖定屏幕上顯示任何內容VISIBILITY_PRIVATE
: 只顯示基本信息,例如通知圖標和內容標題,可是隱藏通知的完整內容。當設置可見性爲VISIBILITY_PRIVATE
的時候,咱們能夠提供通知的備用版本,以隱藏特定的詳情信息,經過設置setPublicVersion(Notification)
來將備用通知附加到普統統知之中:
//設置鎖定屏幕公開範圍爲private並設置通知的備用版本
private fun createPrivateVisibility() {
//建立渠道
createTestStyleChannel()
//建立備用通知
val backupBuilder = mNotificationUtils.createNotification(
testStyleChannelId,
R.drawable.frank,
"新消息",
"收到三條新消息"
)
//建立通知
val person = Person.Builder().run {
setName("Bob")
build()
}
val style = NotificationCompat.MessagingStyle(person).run {
addMessage("Hello", System.currentTimeMillis(), person)
addMessage("World", System.currentTimeMillis(), person)
addMessage("Thanks", System.currentTimeMillis(), person)
}
val builder = mNotificationUtils.createNotification(
testStyleChannelId,
R.drawable.frank,
"新消息",
"收到三條新消息",
style
).run {
setVisibility(NotificationCompat.VISIBILITY_PRIVATE)
}
//設置備用通知
builder.setPublicVersion(backupBuilder.build())
//發送通知
mNotificationUtils.sendNotification(builder.build())
}
複製代碼
經過上面的代碼設置通知以後,若是是鎖屏狀態下就會顯示備用通知,也就是隻顯示"新消息"和"收到三條新消息"的通知,打開鎖屏頁面後就會顯示通知的真正內容,也就是設置的style
中的內容。
有時候咱們可能須要顯示一些緊急消息,好比來電,好比微信視頻通話等,這種狀況下,咱們能夠將全屏Intent
與通知關聯,調用通知的時候,根據設備的鎖定狀態,用戶將會看到如下狀況之一:
Activity
如下代碼演示了將通知和全屏Intent
關聯:
//顯示緊急消息
private fun createUrgentNotification() {
//建立渠道
createTestStyleChannel()
//建立全屏Intent
val intent = Intent(this, ImportantActivity::class.java)
val fullScreenPendingIntent =
PendingIntent.getActivity(this, 10000, intent, PendingIntent.FLAG_UPDATE_CURRENT)
//建立通知
val utils = NotificationUtils.getInstance(this)
val builder =
utils.createNotification(testStyleChannelId, R.drawable.frank, "緊急通知", "Hello World")
//關聯全屏Intent
builder.setFullScreenIntent(fullScreenPendingIntent, true)
//發送通知
utils.sendNotification(builder.build())
}
複製代碼
在實際測試中,除了須要設置上面的屬性,還須要在要打開的Activity
中設置下面的屬性才能在鎖屏頁面直接打開要顯示的Activity
:
override fun onCreate(savedInstanceState: Bundle?) {
requestWindowFeature(Window.FEATURE_NO_TITLE)
//設置如下屬性在鎖屏顯示通知的時候直接打開本頁面
window.addFlags(
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
or WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
or WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
)
super.onCreate(savedInstanceState)
}
複製代碼
須要注意的是,以上代碼的效果僅在個人一加5
手機上測試經過,在其它手機上並無測試過。