Android?Notification使用教程詳解
前言
在應(yīng)用的開(kāi)發(fā)中,我們必然會(huì)接觸到應(yīng)用通知這個(gè)知識(shí),而在通知是隨著Android版本進(jìn)行不斷變化,為了能在高版本和低版本中使用,就需要開(kāi)發(fā)者去做適配,也屬于查漏補(bǔ)缺。了解之前,先看一個(gè)效果圖吧。
正文
通知的使用的內(nèi)容還是比較多的,此篇文章將會(huì)盡可能詳細(xì)的介紹Notification的內(nèi)容。
一、Android中通知的變化
1. Android 4.1,API 16
- 推出了展開(kāi)式通知模板(稱(chēng)為通知樣式),可以提供較大的通知內(nèi)容區(qū)域來(lái)顯示信息。
- 用戶(hù)可以通過(guò)單指向上/向下滑動(dòng)的手勢(shì)來(lái)展開(kāi)通知。還支持以按鈕的形式向通知添加其他操作。
- 允許用戶(hù)在設(shè)置中按應(yīng)用關(guān)閉通知。
2. Android 4.4,API 19 和 20
- 向 API 中添加了通知監(jiān)聽(tīng)器服務(wù)。
- API 級(jí)別 20 中新增了 Android Wear(現(xiàn)已更名為 Wear OS)支持。
3. Android 5.0,API 21
- 推出了鎖定屏幕和提醒式通知。
- 用戶(hù)現(xiàn)在可以將手機(jī)設(shè)為勿擾模式,并配置允許哪些通知在設(shè)備處于“僅限優(yōu)先事項(xiàng)”模式時(shí)打擾他們。
- 向 API 集添加了通知是否在鎖定屏幕上顯示的方法 (setVisibility()),以及指定通知文本的“公開(kāi)”版本的方法。
- 添加了 setPriority() 方法,告知系統(tǒng)通知的“干擾性”(例如,將其設(shè)為“高”可使通知以提醒式通知的形式顯示)。
- 向 Android Wear(現(xiàn)已更名為 Wear OS)設(shè)備添加了通知堆棧支持。使用 setGroup() 將通知放入堆棧。請(qǐng)注意,平板電腦和手機(jī)尚不支持通知堆棧。通知堆棧以后會(huì)稱(chēng)為組或 Bundle。
4. Android 7.0,API 24
- 重新設(shè)置了通知模板的樣式以強(qiáng)調(diào)主打圖片和頭像。
- 添加了三個(gè)通知模板:一個(gè)用于短信應(yīng)用,另外兩個(gè)用于借助展開(kāi)式選項(xiàng)和其他系統(tǒng)裝飾來(lái)裝飾自定義內(nèi)容視圖。
- 向手持設(shè)備(手機(jī)和平板電腦)添加了對(duì)通知組的支持。使用與 Android 5.0(API 級(jí)別 21)中推出的 Android Wear(現(xiàn)已更名為 Wear OS)通知堆棧相同的 API。
- 用戶(hù)可以使用內(nèi)嵌回復(fù)功能直接在通知內(nèi)進(jìn)行回復(fù)(他們輸入的文本將轉(zhuǎn)發(fā)到通知的父應(yīng)用)。
5. Android 8.0,API 26
- 必須將各個(gè)通知放入特定渠道中。
- 用戶(hù)可以按渠道關(guān)閉通知,而非關(guān)閉來(lái)自某個(gè)應(yīng)用的所有通知。
- 包含有效通知的應(yīng)用將在主屏幕/啟動(dòng)器屏幕上相應(yīng)應(yīng)用圖標(biāo)的上方顯示通知“標(biāo)志”。
- 用戶(hù)可以從抽屜式通知欄中暫停某個(gè)通知。您可以為通知設(shè)置自動(dòng)超時(shí)時(shí)間。
- 您還可以設(shè)置通知的背景顏色。
- 部分與通知行為相關(guān)的 API 已從 Notification 移至 NotificationChannel。例如,在搭載 Android 8.0 及更高版本的設(shè)備中,使用 NotificationChannel.setImportance(),而非 NotificationCompat.Builder.setPriority()。
6. Android 12.0,API 31
- 自定義通知,提供通知模板。
- 更改了完全自定義通知的外觀和行為。
下面就開(kāi)始我們的適配之旅吧。
二、創(chuàng)建項(xiàng)目
新建一個(gè)名為NotificationStudy的項(xiàng)目,如下圖所示:
點(diǎn)擊Finish完成項(xiàng)目創(chuàng)建,然后在app的build.gradle中的android{}閉包中開(kāi)啟viewBinding,代碼如下:
buildFeatures { viewBinding true }
項(xiàng)目創(chuàng)建好之后,我們首先改動(dòng)一下activity_main.xml布局。
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <Button android:id="@+id/btn_show" android:layout_width="0dp" android:layout_height="wrap_content" android:text="顯示通知" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
放一個(gè)按鈕,然后再修改一下MainActivity中的代碼,如下所示:
class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) //顯示通知 binding.btnShow.setOnClickListener { } } }
這里就是綁定ViewBinding,然后設(shè)置按鈕點(diǎn)擊監(jiān)聽(tīng),下面我們就要開(kāi)始做顯示通知的操作了。
三、顯示通知
常規(guī)的通知由三個(gè)內(nèi)容構(gòu)成:標(biāo)題、內(nèi)容和圖標(biāo)。在8.0中還有一個(gè)通知渠道,所以我們需要先創(chuàng)建一個(gè)通知渠道。
① 創(chuàng)建通知渠道
創(chuàng)建通知渠道需要三個(gè)參數(shù),渠道ID、渠道名稱(chēng)和渠道重要性。
首先在MainActivity中增加幾個(gè)變量。
//渠道Id private val channelId = "test" //渠道名 private val channelName = "測(cè)試通知" //渠道重要級(jí) private val importance = NotificationManagerCompat.IMPORTANCE_HIGH //通知管理者 private lateinit var notificationManager: NotificationManager //通知 private lateinit var notification: Notification //通知Id private val notificationId = 1
發(fā)送通知首先要通過(guò)通知服務(wù)得到通知管理者,在onCreate方法中增加如下代碼:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) //獲取系統(tǒng)通知服務(wù) notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager ... } }
然后是創(chuàng)建通知渠道,在MainActivity中新增如下函數(shù)。
/** * 創(chuàng)建通知渠道 */ @RequiresApi(Build.VERSION_CODES.O) private fun createNotificationChannel(channelId: String, channelName: String, importance: Int) = notificationManager.createNotificationChannel(NotificationChannel(channelId, channelName, importance))
因?yàn)橥ㄖ朗茿ndroid8.0才有的,因此我們添加一個(gè)注解,然后將數(shù)據(jù)構(gòu)建通知渠道的參數(shù)傳入進(jìn)來(lái),注意我們通過(guò)notificationManager的createNotificationChannel()函數(shù)去創(chuàng)建渠道的,如果notificationManager沒(méi)有初始化的話(huà),毫無(wú)疑問(wèn)你的這一行代碼會(huì)報(bào)錯(cuò)。
② 初始化通知
通知渠道創(chuàng)建好了,下面我們?nèi)コ跏蓟ㄖ?,同樣在MainActivity中新增一個(gè)initNotification()函數(shù),代碼如下:
/** * 初始化通知 */ private fun initNotification() { notification = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //創(chuàng)建通知渠道 createNotificationChannel(channelId,channelName,importance) NotificationCompat.Builder(this, channelId) } else { NotificationCompat.Builder(this) }.apply { setSmallIcon(R.mipmap.ic_launcher)//小圖標(biāo)(顯示在狀態(tài)欄) setLargeIcon(BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher))//大圖標(biāo)(顯示在通知上) setContentTitle("打工人")//標(biāo)題 setContentText("我要搞錢(qián)?。?!")//內(nèi)容 }.build() }
這里首先通過(guò)版本判斷檢查是否需要?jiǎng)?chuàng)建通知渠道,然后會(huì)得到一個(gè)通知的Builder,通過(guò)Builder去配置通知所需要的基本參數(shù),這里我設(shè)置了圖標(biāo),標(biāo)題,內(nèi)容,配置完之后調(diào)用build(),完成通知的構(gòu)建,最后返回一個(gè)notification,現(xiàn)在我們的通知就構(gòu)建好了。
③ 顯示通知
然后我們?cè)傩薷囊幌耾nCreate中的代碼,如下所示
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) //獲取系統(tǒng)通知服務(wù) notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager //初始化通知 initNotification() //顯示通知 binding.btnShow.setOnClickListener { notificationManager.notify(notificationId, notification) } }
這里我們調(diào)用了initNotification()函數(shù),然后在點(diǎn)擊按鈕時(shí)發(fā)送通知。
binding.btnShow.setOnClickListener { notificationManager.notify(notificationId, notification) }
運(yùn)行一下,效果如下圖所示:
四、通知點(diǎn)擊
在上面的gif中,我們成功顯示了通知,其中我們嘗試去做了點(diǎn)擊通知的動(dòng)作,但是沒(méi)有什么反應(yīng),因?yàn)檫@方面的功能還沒(méi)有寫(xiě),下面我們就來(lái)寫(xiě)。
首先要想一下點(diǎn)擊之后要干嘛?通常來(lái)說(shuō)都是跳轉(zhuǎn)到指定的Activity,我們當(dāng)前只有一個(gè)MainActivity,因此我們需要?jiǎng)?chuàng)建一個(gè)。
① 創(chuàng)建目的Activity
我們?cè)赾om.llw.notification下創(chuàng)建DetailsActivity,對(duì)應(yīng)activity_details.xml布局文件,修改一下布局文件,代碼如下:
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".DetailsActivity"> <TextView android:id="@+id/tv_notification_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
這里增加一個(gè)TextView,然后回到DetailsActivity中,修改代碼如下:
class DetailsActivity : AppCompatActivity() { private lateinit var binding: ActivityDetailsBinding @SuppressLint("SetTextI18n") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityDetailsBinding.inflate(layoutInflater) setContentView(binding.root) intent?.let { binding.tvNotificationContent.text = "${it.getStringExtra("title")}\n" + "${it.getStringExtra("content")}" } } }
這里的代碼也很簡(jiǎn)單,就是獲取傳遞過(guò)來(lái)的intent中攜帶的參數(shù)顯示在TextView上,假設(shè)我們?cè)贛ainActivity中點(diǎn)擊時(shí)傳遞title和content。
② PendingIntent使用
我們經(jīng)常使用Intent,但是接觸PendingIntent是比較少的,PendingIntent可以看作是對(duì)Intent的一個(gè)封裝,但它不是立刻執(zhí)行某個(gè)行為,而是滿(mǎn)足某些條件或觸發(fā)某些事件后才執(zhí)行指定的行為。
PendingIntent獲取有三種方式:Activity、Service和BroadcastReceiver獲取。PendingIntent有一個(gè)比較簡(jiǎn)單的使用方式,例如我們現(xiàn)在要在Activity中使用,修改initNotification()函數(shù)中的代碼:
private fun initNotification() { val title = "打工人" val content = "我要搞錢(qián)?。?!" // 為DetailsActivity 創(chuàng)建顯式 Intent val intent = Intent(this, DetailsActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK putExtra("title", title).putExtra("content", content) } val pendingIntent = PendingIntent.getActivity(this, 0, intent, 0) notification = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //創(chuàng)建通知渠道 createNotificationChannel(channelId, channelName, importance) NotificationCompat.Builder(this, channelId) } else { NotificationCompat.Builder(this) }.apply { setSmallIcon(R.mipmap.ic_launcher)//小圖標(biāo)(顯示在狀態(tài)欄) setLargeIcon(BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher))//大圖標(biāo)(顯示在通知上) setContentTitle(title)//標(biāo)題 setContentText(content)//內(nèi)容 setContentIntent(pendingIntent)//設(shè)置內(nèi)容意圖 }.build() }
這里的代碼解釋一下,首先將title、content抽離出來(lái),然后創(chuàng)建intent,再創(chuàng)建pendingIntent 。最后在配置build中設(shè)置setContentIntent(pendingIntent),下面我們運(yùn)行一下。
這里已經(jīng)完成了點(diǎn)擊通知并傳遞的操作,這里還有一個(gè)細(xì)節(jié)就是常規(guī)來(lái)說(shuō)我們點(diǎn)擊了這個(gè)通知表示我們已經(jīng)看到了,或者已經(jīng)知曉了內(nèi)容,因此通知就會(huì)消失,而這里通知并沒(méi)有消息。
只需要一個(gè)配置就可以做到。
setAutoCancel(true)//設(shè)置自動(dòng)取消
添加位置如下圖所示:
運(yùn)行測(cè)試一下就行,我就不用gif說(shuō)明了。
五、折疊通知
有時(shí)候我們?cè)O(shè)置通知的內(nèi)容可能一行放不下,這個(gè)時(shí)候就需要收縮和展開(kāi)通知,讓用戶(hù)看到完整的信息。
① 長(zhǎng)文本
現(xiàn)在我將content的內(nèi)容修改一下:
val content = "我要搞錢(qián)?。?!富強(qiáng)、明主、文明、和諧、自由、平等、公正、法治、愛(ài)國(guó)、敬業(yè)、誠(chéng)信、友善"
然后我們什么都不做去運(yùn)行看看。
這里并沒(méi)有顯示多行,同時(shí)也沒(méi)有那個(gè)向下展開(kāi)的按鈕,內(nèi)容文字做了省略,因此這說(shuō)明那個(gè)展開(kāi)需要我們?nèi)ピO(shè)置。
一行代碼搞定:
setStyle(NotificationCompat.BigTextStyle().bigText(content))
通過(guò)設(shè)置通知的風(fēng)格樣式,這里使用的是長(zhǎng)文本信息樣式,將content傳遞進(jìn)去。添加位置如下圖所示:
運(yùn)行一下看看
② 顯示圖片
有時(shí)候我們會(huì)在通知中展開(kāi)時(shí)看到圖片,實(shí)際上就是使用了另一個(gè)樣式,也是一行代碼解決。
setStyle(NotificationCompat.BigPictureStyle().bigPicture(BitmapFactory.decodeResource(resources,R.drawable.logo)))//設(shè)置樣式
這行代碼的位置和替換掉剛才哪一行,通知只能設(shè)置一個(gè)樣式,后面設(shè)置的樣式會(huì)覆蓋掉前面的樣式,這里我們使用大圖片樣式,傳進(jìn)入一個(gè)logo.jpg圖片,你在寫(xiě)的時(shí)候隨便用什么都行,然后我們?cè)龠\(yùn)行一下。
六、回復(fù)通知
有時(shí)候我們的手機(jī)收到短信消息,是可以直接回復(fù)的。當(dāng)然了這個(gè)功能是需要手動(dòng)去寫(xiě)的。流程就是點(diǎn)擊按鈕出現(xiàn)一個(gè)通知,通知中回復(fù)消息,廣播或服務(wù)收到消息后更新通知,然后關(guān)閉通知。
① 創(chuàng)建廣播
這里我們先來(lái)寫(xiě)這個(gè)接收回復(fù)消息的處理,這里用廣播來(lái)處理,首先我們需要?jiǎng)?chuàng)建一個(gè)廣播,在com.llw.notification下新建一個(gè)ReplyMessageReceiver類(lèi),里面的代碼如下:
class ReplyMessageReceiver : BroadcastReceiver() { private val TAG = ReplyMessageReceiver::class.java.simpleName override fun onReceive(context: Context, intent: Intent) { //獲取回復(fù)消息的內(nèi)容 val inputContent = RemoteInput.getResultsFromIntent(intent)?.getCharSequence("key_text_reply")?.toString() Log.d(TAG, "onReceive: $inputContent") if (inputContent == null) { Log.e(TAG, "onReceive: 沒(méi)有回復(fù)消息!") return } //構(gòu)建回復(fù)消息通知 val repliedNotification = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationCompat.Builder(context, "reply") } else { NotificationCompat.Builder(context) }.apply { setSmallIcon(R.mipmap.ic_launcher)//小圖標(biāo)(顯示在狀態(tài)欄) setContentTitle("1008666")//標(biāo)題 setContentText("消息發(fā)送成功!")//內(nèi)容 }.build() val notificationManager = context.getSystemService(AppCompatActivity.NOTIFICATION_SERVICE) as NotificationManager //發(fā)送通知 notificationManager.notify(2, repliedNotification) //1秒后取消通知 Timer().schedule(1000){ notificationManager.cancel(2) } } }
這里說(shuō)明一下:首先是RemoteInput表示遠(yuǎn)程輸入,也就是通知欄上輸入框,這里就是獲取輸入框的內(nèi)容,注意"key_text_reply"這個(gè)值,我們?cè)跇?gòu)建RemoteInput時(shí)使用的值要與這個(gè)一致,不一致你在廣播中就拿不到輸入的值。
然后是構(gòu)建通知了,這里的設(shè)置都是常規(guī)操作,渠道id我是寫(xiě)死的,因此在Activity中創(chuàng)建通知時(shí)渠道Id也要一致。最后就是在發(fā)送通知之后加了一個(gè)1秒的延時(shí)去取消通知,表示收到回復(fù)的處理。
廣播是需要在AndroidManifest.xml注冊(cè)的,代碼如下:
<receiver android:name=".ReplyMessageReceiver"/>
位置如下:
下面就是要構(gòu)建回復(fù)通知了。因?yàn)橐獏^(qū)別于之前的普通通知,所以這里我需要更改一下activity_main.xml中的布局代碼
<Button android:id="@+id/btn_show_reply" android:layout_width="0dp" android:layout_height="wrap_content" android:text="顯示回復(fù)通知" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/btn_show" />
增加一個(gè)按鈕,點(diǎn)擊事件后面再寫(xiě)。
② RemoteInput
為了區(qū)分普通通知和回復(fù)通知,在MainActivity中定義一下。
//回復(fù)通知Id private val replyNotificationId = 2 //回復(fù)通知 private lateinit var replyNotification: Notification
下面我們新建一個(gè)initReplyNotification()函數(shù),在里面對(duì)RemoteInput進(jìn)行構(gòu)建。
private fun initReplyNotification() { //遠(yuǎn)程輸入 val remoteInput = RemoteInput.Builder("key_text_reply").setLabel("快速回復(fù)").build() }
這里RemoteInput的構(gòu)建也很簡(jiǎn)單,最關(guān)鍵的就是"key_text_reply",剛才在適配器中已經(jīng)說(shuō)了,而Label就是一個(gè)輸入框的提示文本。
③ PendingIntent
現(xiàn)在我們要通過(guò)BroadcastReceiver獲取PendingIntent,在initReplyNotification()函數(shù)中新增代碼:
private fun initReplyNotification() { ... //構(gòu)建回復(fù)pendingIntent val replyIntent = Intent(this, ReplyMessageReceiver::class.java) val pendingIntent = PendingIntent.getBroadcast(this, 0, replyIntent, PendingIntent.FLAG_ONE_SHOT) }
④ NotificationCompat.Action
通知?jiǎng)幼?,我們?cè)谳斎肟驅(qū)懭胄畔ⅲ枰粋€(gè)按鈕發(fā)送這個(gè)按鈕,這個(gè)Action就是用來(lái)做這個(gè)事情的,在initReplyNotification()函數(shù)中新增代碼:
private fun initReplyNotification() { ... //構(gòu)建回復(fù)pendingIntent val replyIntent = Intent(this, ReplyMessageReceiver::class.java) val pendingIntent = PendingIntent.getBroadcast(this, 0, replyIntent, PendingIntent.FLAG_ONE_SHOT) }
⑤ 構(gòu)建通知
下面就是構(gòu)建通知,在initReplyNotification()函數(shù)中新增代碼:
private fun initReplyNotification() { ... //構(gòu)建通知 replyNotification = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { createNotificationChannel("reply", "回復(fù)消息", importance) NotificationCompat.Builder(this, "reply") } else { NotificationCompat.Builder(this) }.apply { setSmallIcon(R.mipmap.ic_launcher)//小圖標(biāo)(顯示在狀態(tài)欄) setLargeIcon(BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher))//大圖標(biāo)(顯示在通知上) setContentTitle("1008666")//標(biāo)題 setContentText("你的賬號(hào)已欠費(fèi)2000元!")//內(nèi)容 addAction(action) }.build() }
這里的關(guān)鍵就在于這個(gè)addAction(action),下面我們運(yùn)行一下:
然后我們看看日志:
七、橫幅通知
橫幅通知我相信你一定見(jiàn)過(guò),例如收到短信、QQ、微信、釘釘?shù)南?,都?huì)有出現(xiàn)在屏幕頂部,主要是用于提醒用戶(hù)的。
首先我們修改布局,在activity_main.xml中新增一個(gè)按鈕,代碼如下:
<Button android:id="@+id/btn_show_banner" android:layout_width="0dp" android:layout_height="wrap_content" android:text="顯示橫幅通知" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/btn_show_reply" />
① 檢查橫幅通知是否打開(kāi)
首先在MainActivity中定義幾個(gè)變量
//橫幅通知 private lateinit var bannerNotification: Notification //橫幅通知Id private val bannerNotificationId = 3 //開(kāi)啟橫幅通知返回 private val bannerLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { if (it.resultCode == RESULT_OK) { Log.d("TAG", "返回結(jié)果") } }
然后新增一個(gè)openBannerNotification()函數(shù),代碼如下:
private fun openBannerNotification() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val bannerImportance = notificationManager.getNotificationChannel("banner").importance if (bannerImportance == NotificationManager.IMPORTANCE_DEFAULT) { val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS) .putExtra(Settings.EXTRA_APP_PACKAGE, packageName) .putExtra(Settings.EXTRA_CHANNEL_ID, "banner") bannerLauncher.launch(intent); false } else true } else true
這里通過(guò)檢查通知遇到的重要級(jí)判斷是否開(kāi)啟橫幅通知。
② 構(gòu)建橫幅通知渠道
在MainActivity中新增createBannerNotificationChannel()函數(shù),代碼如下:
@RequiresApi(Build.VERSION_CODES.O) private fun createBannerNotificationChannel(channelId: String, channelName: String, importance: Int) = notificationManager.createNotificationChannel( NotificationChannel(channelId, channelName, importance).apply { description = "提醒式通知"http://渠道描述 enableLights(true)//開(kāi)啟閃光燈 lightColor = Color.BLUE//設(shè)置閃光燈顏色 enableVibration(true)//開(kāi)啟震動(dòng) vibrationPattern = longArrayOf(0, 1000, 500, 1000)//震動(dòng)模式 setSound(null, null)//沒(méi)有提示音 } )
這里的內(nèi)容相對(duì)于之前來(lái)說(shuō)就多一些,有注釋也好理解。
③ 構(gòu)建橫幅通知
在MainActivity中新增initBannerNotification()函數(shù),代碼如下:
private fun initBannerNotification() { //構(gòu)建通知 bannerNotification = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { createBannerNotificationChannel("banner", "提醒消息", importance) NotificationCompat.Builder(this, "banner") } else { NotificationCompat.Builder(this) }.apply { setSmallIcon(R.mipmap.ic_launcher)//小圖標(biāo)(顯示在狀態(tài)欄) setLargeIcon(BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher))//大圖標(biāo)(顯示在通知上) setContentTitle("落魄Android在線(xiàn)炒粉")//標(biāo)題 setContentText("不要9塊9,不要6塊9,只要3塊9。")//內(nèi)容 setWhen(System.currentTimeMillis())//通知顯示時(shí)間 setAutoCancel(true)//設(shè)置自動(dòng)取消 }.build() }
這里也就是一些常規(guī)的設(shè)置。
④ 顯示橫幅通知
最后我們?cè)趏nCreate()函數(shù)中,新增如下代碼:
//顯示橫幅通知 binding.btnShowBanner.setOnClickListener { //檢查是否授予訪(fǎng)問(wèn)權(quán)限 if (openBannerNotification()) { notificationManager.notify(bannerNotificationId, bannerNotification) } }
下面我們運(yùn)行一下:
OK,這樣就可以了。
八、常駐通知
我們知道有一些通知,當(dāng)程序運(yùn)行的時(shí)候就會(huì)出現(xiàn),例如我們最常見(jiàn)的音樂(lè)類(lèi)App,而且這個(gè)通知并不是馬上出現(xiàn)的,在此之前還有很多內(nèi)容要初始化,一切就緒之后出現(xiàn)這個(gè)通知,就可以通過(guò)通知去控制音樂(lè)了。
我們并不需要這種復(fù)雜的操作,只有有一個(gè)通知能在App啟動(dòng)的時(shí)候顯示出來(lái),并且App進(jìn)入后臺(tái)時(shí),通知也還在。
在MainActivity創(chuàng)建變量。
//常駐通知 private lateinit var permanentNotification: //常駐通知Id private val permanentNotificationId = 4Notification
然后在MainActivity中新增一個(gè)showPermanentNotification()函數(shù),代碼如下:
private fun showPermanentNotification() { //構(gòu)建回復(fù)pendingIntent val permanentIntent = Intent(this, MainActivity::class.java) val pendingIntent = PendingIntent.getActivity(this, 0, permanentIntent, PendingIntent.FLAG_UPDATE_CURRENT) //構(gòu)建通知 permanentNotification = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { createNotificationChannel("permanent", "我一直存在", importance) NotificationCompat.Builder(this, "permanent") } else { NotificationCompat.Builder(this) }.apply { setSmallIcon(R.mipmap.ic_launcher)//小圖標(biāo)(顯示在狀態(tài)欄) setLargeIcon(BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher))//大圖標(biāo)(顯示在通知上) setContentTitle("你在努力些什么?")//標(biāo)題 setContentText("搞錢(qián)!搞錢(qián)!還是搞錢(qián)!")//內(nèi)容 setWhen(System.currentTimeMillis())//通知顯示時(shí)間 setContentIntent(pendingIntent) }.build() permanentNotification.flags = Notification.FLAG_ONGOING_EVENT notificationManager.notify(permanentNotificationId, permanentNotification) }
這里就很簡(jiǎn)單了,主要就是去掉通知取消設(shè)置,同時(shí)設(shè)置FLAG_ONGOING_EVENT,另外在點(diǎn)擊通知的時(shí)候進(jìn)入主頁(yè)面。在onCreate()函數(shù)中調(diào)用。
運(yùn)行一下:
可以看到這里我用別的通知顯示出來(lái)之后,清理一下,其他通知就沒(méi)有了,而常駐通知還在,然后我程序進(jìn)入后臺(tái),點(diǎn)擊常駐通知,又會(huì)啟動(dòng)到前臺(tái)。
九、自定義樣式通知
現(xiàn)在我們使用的都是常規(guī)的樣式通知,實(shí)際上我們可以自定義的,就是自定義通知布局,我們先來(lái)設(shè)計(jì)自定義布局的樣式,就做一個(gè)音樂(lè)通知欄吧,首先是三個(gè)圖標(biāo)。
① 自定義通知布局
在drawable文件夾下新建ic_previous.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="36dp" android:height="36dp" android:tint="@color/white" android:viewportWidth="24.0" android:viewportHeight="24.0"> <path android:fillColor="@android:color/white" android:pathData="M7,6c0.55,0 1,0.45 1,1v10c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1L6,7c0,-0.55 0.45,-1 1,-1zM10.66,12.82l5.77,4.07c0.66,0.47 1.58,-0.01 1.58,-0.82L18.01,7.93c0,-0.81 -0.91,-1.28 -1.58,-0.82l-5.77,4.07c-0.57,0.4 -0.57,1.24 0,1.64z" /> </vector>
ic_play.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="36dp" android:height="36dp" android:tint="@color/white" android:viewportWidth="24.0" android:viewportHeight="24.0"> <path android:fillColor="@android:color/white" android:pathData="M8,6.82v10.36c0,0.79 0.87,1.27 1.54,0.84l8.14,-5.18c0.62,-0.39 0.62,-1.29 0,-1.69L9.54,5.98C8.87,5.55 8,6.03 8,6.82z" /> </vector>
ic_next.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="36dp" android:height="36dp" android:tint="@color/white" android:viewportWidth="24.0" android:viewportHeight="24.0"> <path android:fillColor="@android:color/white" android:pathData="M7.58,16.89l5.77,-4.07c0.56,-0.4 0.56,-1.24 0,-1.63L7.58,7.11C6.91,6.65 6,7.12 6,7.93v8.14c0,0.81 0.91,1.28 1.58,0.82zM16,7v10c0,0.55 0.45,1 1,1s1,-0.45 1,-1V7c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1z" /> </vector>
然后在layout下新建一個(gè)layout_custom_notification.xml,代碼如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/black" android:gravity="center_vertical"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/tv_song_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:text="雨下一整晚" android:textColor="@color/white" android:textSize="16sp" /> <TextView android:id="@+id/tv_singer" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="4dp" android:text="周杰倫" android:textColor="@color/white" android:textSize="12sp" /> </LinearLayout> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center"> <ImageButton android:id="@+id/iv_previous" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@null" android:src="@drawable/ic_previous" /> <ImageButton android:id="@+id/iv_play" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginEnd="16dp" android:background="@null" android:src="@drawable/ic_play" /> <ImageButton android:id="@+id/iv_next" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@null" android:src="@drawable/ic_next" /> </LinearLayout> <ImageView android:id="@+id/iv_avatar" android:layout_width="72dp" android:layout_height="72dp" android:src="@drawable/jay" /> </LinearLayout>
這里要注意一點(diǎn)自定義通知的界面布局只支持LinearLayout、RelativeLayout、FrameLayout,目前不支持ConstraintLayout通知布局里有ConstraintLayout的話(huà),彈通知不會(huì)顯示。
jpg圖標(biāo)用自己的,或者用我源碼里面都可以,然后就很簡(jiǎn)單了,回到MainActivity。
② 顯示自定義通知
//自定義通知 private lateinit var customNotification: Notification //自定義通知Id private val customNotificationId = 5
然后創(chuàng)建initCustomNotification()函數(shù),代碼如下:
@SuppressLint("RemoteViewLayout") private fun initCustomNotification() { //RemoteView val remoteViews = RemoteViews(packageName, R.layout.layout_custom_notification) customNotification = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { createNotificationChannel("custom", "自定義通知", importance) NotificationCompat.Builder(this, "custom") } else { NotificationCompat.Builder(this) }.apply { setSmallIcon(R.mipmap.ic_launcher)//小圖標(biāo)(顯示在狀態(tài)欄) setCustomContentView(remoteViews)//設(shè)置自定義內(nèi)容視圖 setVisibility(NotificationCompat.VISIBILITY_PUBLIC) setOnlyAlertOnce(true) setOngoing(true) }.build() }
在onCreate中調(diào)用
然后我們?cè)赼ctivity_main.xml中新增一個(gè)按鈕:
<Button android:id="@+id/btn_show_custom" android:layout_width="0dp" android:layout_height="wrap_content" android:text="顯示自定義通知" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/btn_show_banner" />
最后在MainActivity中的onCreate()函數(shù)中新增按鈕點(diǎn)擊事件,同時(shí)點(diǎn)擊調(diào)用通知顯示:
//顯示自定義通知 binding.btnShowCustom.setOnClickListener { notificationManager.notify(customNotificationId, customNotification) }
運(yùn)行一下:
③ 自定義通知大小
通知布局視圖布局的高度上限為 64 dp,展開(kāi)后的視圖布局的高度上限為 256 dp,剛才我們只設(shè)置了小的通知,那么如果要展開(kāi)一個(gè)大一點(diǎn)的通知,最好是能夠滑動(dòng)通知的時(shí)候有大小變化。
其實(shí)很簡(jiǎn)單,首先我們同樣要定義一個(gè)大一點(diǎn)同通知布局,在layout下新建一個(gè)layout_custom_notification_big.xml,代碼如下:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/black" android:gravity="center_vertical"> <TextView android:id="@+id/tv_song_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:text="雨下一整晚" android:textColor="@color/white" android:textSize="20sp" /> <TextView android:id="@+id/tv_singer" android:layout_below="@+id/tv_song_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="6dp" android:text="周杰倫" android:textColor="@color/white" android:textSize="16sp" /> <LinearLayout android:layout_marginTop="16dp" android:layout_below="@+id/tv_singer" android:layout_alignParentStart="true" android:layout_toStartOf="@+id/iv_avatar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center"> <ImageButton android:id="@+id/iv_previous" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@null" android:src="@drawable/ic_previous" /> <ImageButton android:id="@+id/iv_play" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginEnd="16dp" android:background="@null" android:src="@drawable/ic_play" /> <ImageButton android:id="@+id/iv_next" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@null" android:src="@drawable/ic_next" /> </LinearLayout> <ImageView android:layout_alignParentEnd="true" android:id="@+id/iv_avatar" android:layout_width="144dp" android:layout_height="144dp" android:src="@drawable/jay" /> </RelativeLayout>
然后我們修改initCustomNotification()函數(shù)中的代碼:
@SuppressLint("RemoteViewLayout") private fun initCustomNotification() { //RemoteView val remoteViews = RemoteViews(packageName, R.layout.layout_custom_notification) val bigRemoteViews = RemoteViews(packageName, R.layout.layout_custom_notification_big) customNotification = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { createNotificationChannel("custom", "自定義通知", importance) NotificationCompat.Builder(this, "custom") } else { NotificationCompat.Builder(this) }.apply { setSmallIcon(R.mipmap.ic_launcher)//小圖標(biāo)(顯示在狀態(tài)欄) setVisibility(NotificationCompat.VISIBILITY_PUBLIC) setCustomContentView(remoteViews) setCustomBigContentView(bigRemoteViews) setOnlyAlertOnce(true) setOngoing(true) }.build() }
我們?cè)賱?chuàng)建一個(gè)RemoteView,然后通過(guò)setCustomBigContentView設(shè)置一下就可以了,下面運(yùn)行一下,看看效果。
十、源碼
如果你覺(jué)得代碼對(duì)你有幫助的話(huà),不妨Fork或者Star一下~
GitHub:NotificationStudy
到此這篇關(guān)于Android Notification使用的文章就介紹到這了,更多相關(guān)Android Notification使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Android使用Notification在狀態(tài)欄上顯示通知
- Android編程使用Service實(shí)現(xiàn)Notification定時(shí)發(fā)送功能示例
- Android 使用AlarmManager和NotificationManager來(lái)實(shí)現(xiàn)鬧鐘和通知欄
- android使用NotificationListenerService監(jiān)聽(tīng)通知欄消息
- Android種使用Notification實(shí)現(xiàn)通知管理以及自定義通知欄實(shí)例(示例四)
- Android使用Notification實(shí)現(xiàn)普通通知欄(一)
- Android中通知Notification使用實(shí)例(振動(dòng)、燈光、聲音)
相關(guān)文章
Android開(kāi)發(fā)Activity的生命周期詳解
這篇文章主要介紹了Android開(kāi)發(fā)Activity的生命周期詳解,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參一下2022-07-07Hook實(shí)現(xiàn)Android 微信、陌陌 、探探位置模擬(附源碼下載)
這篇文章主要介紹了Hook實(shí)現(xiàn)Android 微信、陌陌 、探探位置模擬(附源碼下載)的相關(guān)資料,需要的朋友可以參考下2017-03-03Android Messenger實(shí)現(xiàn)進(jìn)程間通信及其原理
這篇文章主要為大家詳細(xì)介紹了Android Messenger實(shí)現(xiàn)進(jìn)程間通信及其原理,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-05-0521天學(xué)習(xí)android開(kāi)發(fā)教程之MediaPlayer
21天學(xué)習(xí)android開(kāi)發(fā)教程之MediaPlayer,MediaPlayer可以播放音頻和視頻,操作相對(duì)簡(jiǎn)單,感興趣的小伙伴們可以參考一下2016-02-02Android自定義View制作動(dòng)態(tài)炫酷按鈕實(shí)例解析
這篇文章主要為大家詳細(xì)解析了Android自定義View制作動(dòng)態(tài)炫酷按鈕實(shí)例,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-07-07Android Studio報(bào)錯(cuò)unable to access android sdk add-on list解決方案
這篇文章主要介紹了Android Studio報(bào)錯(cuò)unable to access android sdk add-on list解決方案,本文通過(guò)多種方式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03android高仿微信表情輸入與鍵盤(pán)輸入代碼(詳細(xì)實(shí)現(xiàn)分析)
表情與鍵盤(pán)的切換輸入大部分IM都會(huì)需要到,本篇文章主要介紹了android高仿微信表情輸入與鍵盤(pán)輸入,具有一定的參考價(jià)值,有興趣的可以了解一下。2016-12-12根據(jù)USER-AGENT判斷手機(jī)類(lèi)型并跳轉(zhuǎn)到相應(yīng)的app下載頁(yè)面
檢測(cè)瀏覽器的USER-AGENT,然后根據(jù)正則表達(dá)式來(lái)確定客戶(hù)端類(lèi)型,并跳轉(zhuǎn)到相應(yīng)的app下載頁(yè)面,這個(gè)方法還是比較實(shí)用的,大家可以看看2014-09-09