Android Notification詳解
原文轉載來自https://my.oschina.net/Denua/blog/2052050
這篇文章如果還是沒有解決你的問題不妨在看看這篇 https://www.jianshu.com/p/cb8426620e74
通知欄通知在Android APP中的使用極爲頻繁,比如短信通知,QQ,微信消息通知,App 更新進度轉態顯示,截圖時後圖片進行刪除或分享,查看操作等等。本篇文章記錄瞭如何使用 Notification 顯示消息, 設置提示音,呼吸燈,震動,以及響應用戶對消息的處理動作。
Notification 狀態欄通知
- 建造者模式構建通知類: Notification.Builder
- 通知管理器(用於發出通知): NotificationManager
- 通知通道(API 26新增,用戶可以選擇性屏蔽通知):NotificationChannel
- 通知動作(用戶點擊滑動通知等):PaddingIntent
發送通知的步驟
- 獲取 NotificationManager
- 創建 Notification
- 給 Notification 設置參數
- 使用 NotificationManager 發送通知
Android 7.0 新內容
Notification.Builder.serPriority: 設置通知優先級 Notification.Builder.setStyle: 設置通知擴展布局 MessagingStyle: 快速回復
Android 8.0 新內容
NotificationChannel:用戶可以自定義通知的顯示,以及關閉某個通知的提示音震動等
構建一個最簡單的 Notification
一個簡單的通知包含,一個小圖標,一個標題和內容如圖:
public static final int NOTIFICATION_ID = 0x111;
// 獲取系統 通知管理 服務
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// 構建 Notification
Notification.Builder builder = new Notification.Builder(this);
builder.setContentTitle("Not標題")
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentText("Not內容");
// 兼容 API 26,Android 8.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
// 第三個參數表示通知的重要程度,默認則只在通知欄閃爍一下
NotificationChannel notificationChannel = new NotificationChannel("AppTestNotificationId", "AppTestNotificationName", NotificationManager.IMPORTANCE_DEFAULT);
// 註冊通道,註冊後除非卸載再安裝否則不改變
notificationManager.createNotificationChannel(notificationChannel);
builder.setChannelId("AppTestNotificationId");
}
// 發出通知
notificationManager.notify(NOTIFICATION_ID, builder.build());
控制 Notification 的震動,呼吸燈、提示音以及如何顯示
構建 Notification 代碼
// 構建 Notification
Notification.Builder builder = new Notification.Builder(this);
builder.setContentTitle("ContentTitle")
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentText("Content Text Here")
.setDefaults(Notification.DEFAULT_ALL);
這裏關鍵的在於 SetDefaults 這個方法,它接收一個 int,可選值如下:
- Notification.DEFAULT_VIBRATE :震動提示
- Notification.DEFAULT_SOUND:提示音
- Notification.DEFAULT_LIGHTS:三色燈
- Notification.DEFAULT_ALL:以上三種全部
控制震動時長
setVibrate 接收一個long[], 下標爲奇數爲延時,偶數爲震動時長,本例中爲,延時0ms,震動300ms,延時200ms,震動300
builder.setVibrate(new long[]{0, 300, 200, 300});
// Android 8.0 需用以下方法
// notificationChannel.enableVibration(true);
// notificationChannel.setVibrationPattern(new long[]{0,300,300,500,300,800});
控制燈顏色
三個參數依次是 int ARGB 顏色值、亮的時長、不亮的時長。
builder.setLights(0xFFFF0000, 1000,1000);
// Android 8.0 需用以下方法,不可設置時長
// notificationChannel.enableLights(true);
// notificationChannel.setLightColor(0xFFFF0000);
控制聲音
builder.setSound(Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.a))
// Android 8.0 需用以下方法, 這裏用的是加載res/raw的聲音資源
// notificationChannel.setSound(Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.a), null);
在鎖屏上的顯示方式
這個方法需要使用鎖屏並在設置中隱藏敏感信息才能生效
builder.setVisibility(Notification.VISIBILITY_PUBLIC);
// Android 8.0 需用一下方法
// notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
這個方法接收一個 int
- Notification.VISIBILITY_PUBLIC 顯示所有通知內容
- Notification.VISIBILITY_PRIVATE 只顯示標題
- Notification.VISIBILITY_SECRET 不顯示任何內容
如何取消通知
- 用戶清除
- setAutoCancel(),點擊通知會清除
- NotificationManager.cancel(int id)
- NotificationManager.cancel(String tag, int id)
- NotificationManager.cancelAll() 清除所有該應用的通知
其他
持續的通知
如播放音樂,後臺任務,下載, 當這個方法傳入 true 時,表示它是一個持續的通知,用戶無法刪除它,只能在代碼中讓通知消失。
builder.setOngoing(true);
顯示進度條
在後臺處理某個耗時任務時需要使用到進度條, 參數作用依次是 進度條最大值、當前進度、進度是否確定,indeterminate 表示任務的進度是否可以準確獲取
builder.setProgress(int max, int progress, boolean indeterminate);
如何更新通知
只需在發出通知時使用相同的 id 即可更新,如果這條通知已被移除則創建一個新的通知
notificationManager.notify(int id, Notification.Builder);
如何響應用戶動作
給 Notification 設置 PendingIntent 用於響應點擊、清除動作
PendingIntent 是一種特殊的 Intent, 作用和 Intent 一樣是用於啓動一個 Activity或者Service,或發送一條 Broadcast。通知響應用戶動作便是用這個, 當對通知做出一個動作後,系統便會調用 PendingIntent,啓動一個活動,服務或廣播,這取決於你獲取的是那種 PendingIntent。
在 PendingIntent 中傳入的 Context 銷燬以後,PendingIntent 依舊有效,它一般使用在當 Context 銷燬後需要執行 Intent的地方,一般不是用於立即執行的時候,比如在點擊通知後喚醒一個 Activity。。
獲取 PendingIntent
// 獲取用於啓動 Activity
PendingIntent.getActivity(Context context, int requestCode, Intent intent, int flags);
// 獲取用於發送 Broadcast
PendingIntent.getBroadcast(Context context, int requestCode, Intent intent, int flags);
// 獲取用於啓動服務
PendingIntent.getService(Context context, int requestCode, Intent intent, int flags);
PendingIntent.getActivities(Context context, int reqeustCode, Intent[] intents, int flags);
PendingIntent.getForgroundService(Context, int reqeustCode, Intent intent, int flags)
各個參數的用途分別如下
- context, 當前上下文
- requestCode, 請求標識,如果多次獲取時這個值相同,則返回的結果與 flags 參數相關
- intent, 請求意圖
- flags, 控制獲取 PendingIntent 的方式,可選值如下
- PendingIntent.FLAG_CANCEL_CURRENT,如果當前已存在則取消當前的並返回一個新的 PendingIntent
- PendingIntent.FLAG_UPDATE_CURRENT,如果已存在則更新之前的
- PendingIntent.FLAG_NO_CREATE,如果已存在則返回當前存在的,否則返回 null
- PendingIntent.FLAG_ONE_SHOT,表明這個 PendingIntent 只能用一次,觸發一次後自動 cancel
- PendingIntent.FLAG_IMMUTABLE,表明這個PendingIntent 不可變
其中,requestCode 和 flags 是相關聯的,如果多次獲取 PendingIntent 時 requestCode 相同,此時返回的結果就需要參考 flags 的值。例如
Intent intent1 = new Intent("NotificationAction");
intent1.putExtra("A", "value AA");
intent1.putExtra("B", "value BB");
Intent intent11 = new Intent("NotificationAction");
intent11.putExtra("B", "BBB");
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 1, intent1, PendingIntent.FLAG_CANCEL_CURRENT);
PendingIntent pendingIntent11 = PendingIntent.getBroadcast(this, 1, intent11, PendingIntent.FLAG_UPDATE_CURRENT);
這兩個 PendingIntent 的 requestCode 是相同的,第二個獲取時傳入的 flags 爲 PendingIntent.FLAG_UPDATE_CURRENT ,表示存在 requestCode 爲 1,的PendingIntent則更新這個PendingIntent 的 Intent 值,此時 getStringExtr("A") 的值爲 null,getStringExtra("B"), 的值爲 BBB。
當我們獲取了 PendingIntent 之後,只需要給 builder 的特定方法傳入就可以響應點擊、清除動作了
設置點擊,清除 PendingIntent 代碼:
builder.setContentIntent(PendingIntent intent);
builder.setDeleteIntent(PendingIntent intent);
添加幾個簡單的按鈕
在需要對通知內容進行簡單操作的時候,比如短信的‘標記爲已讀’,‘刪除’。我們可以對按鈕設置圖標、標題以及設置 PendingIntent 以便響應動作。這種按鈕最多可以添加三個,按添加的順序從左往右依次排列。 api 23 之後圖標不顯示
Intent intent2 = new Intent("NotificationAction");
intent2.putExtra("A", "Action button1 clicked");
PendingIntent pendingIntent1 = PendingIntent.getBroadcast(this, 3, intent2, PendingIntent.FLAG_UPDATE_CURRENT);
otification.Action.Builder actionBuilder = new Notification.Action.Builder(Icon.createWithResource(getApplicationContext(), R.drawable.ic_launcher_background), "Action1", pendingIntent1);
builder.addAction(actionBuilder.build());
結果如圖,點擊 Action1 按鈕後將發出一條廣播
超長內容的 Notification, 各種 Style
超長內容的 Notification,比如內容爲文字很多,或者一個大圖片,一個自定義的 View,通過 builder.setStyle(Style style) 這個方法設置。設置 style 之後,之前的 setContent 設置的內容將會失效。各種 style 的高度最大爲 256 dp。
BigTextStyle,超長文本
Notification.BigTextStyle bigTextStyle = new Notification.BigTextStyle().bigText(
"This is big text notification. This is big text notification. " +
"This is big text notification. This is big text notification. " +
"This is big text notification. This is big text notification. " +
"This is big text notification. This is big text notification. " +
"This is big text notification. This is big text notification. " +
"This is big text notification. This is big text notification. " +
"This is big text notification. This is big text notification. " +
"This is big text notification. This is big text notification. " +
"This is big text notification. This is big text notification. ");
builder.setStyle(bigTextStyle);
InboxStyle,列表
inboxstyle 顯示爲一個文字列表,顯示 addline 的順序的文字,最多支持六行。
Notification.InboxStyle inboxStyle = new Notification.InboxStyle();
// 添加行
inboxStyle.addLine("First line.");
inboxStyle.addLine("Second line");
inboxStyle.addLine("Third line");
inboxStyle.addLine("Last line");
// 設置標題以及簡介文字
inboxStyle.setBigContentTitle("ContentTitle");
inboxStyle.setSummaryText("SummaryText");
builder.setStyle(inboxStyle);
BigPictureStyle, 大圖片
// 構建一個 Style
Notification.BigPictureStyle bigPictureStyle =
new Notification.BigPictureStyle().bigPicture(BitmapFactory.decodeResource(getResources(),R.drawable.big));
// 設置標題
bigPictureStyle.setBigContentTitle("ContentTitle");
// 設置副標題,簡介文字
bigPictureStyle.setSummaryText("SummaryText");
// 設置大圖標
bigPictureStyle.bigLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.big));
builder.setStyle(bigPictureStyle);
MediaStyle
略
快速回復 MessageNotification
有些時候我們需要對通知內容進行快速回復,比如收到一條郵件,我們可以快速回復。當點擊通知的一個 Action 按鈕後將會出現一個輸入框,可以進行發送消息。我們需要添加一個 MessageStyle 和 一個帶 RemoteInput 的 Action,這個通知便能支持快速回復。這個功能需要 api 版本大於 24
首先,構建一個 MessageStyle,並設置好
Notification.MessagingStyle messagingStyle = new Notification
.MessagingStyle("UserDisplayName").
addMessage("MessageStyle", 1000, "sender");
builder.setStyle(messagingStyle);
然後再創建一個 Action,Action 需添加一個 RemoteInput,一個響應發送動作的 PendingIntent,這個例子中點擊發送按鈕後將發送一條廣播。
Intent intent = new Intent("NotificationAction");
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 11, intent, PendingIntent.FLAG_UPDATE_CURRENT);
RemoteInput remoteInput = new RemoteInput
.Builder("RemoteInputKey")
.setLabel("RemoteInputLabel")
.build();
Notification.Action action = new Notification.Action
.Builder(Icon.createWithResource(this, R.mipmap.ic_launcher_round), "ReplayAction", pendingIntent)
.addRemoteInput(remoteInput)
.build();
// 發送該通知
notificationManager.notify(22, builder.build());
接受輸入數據用的廣播,我們在獲取到輸入數據後需要手動取消該通知,否則不會自動取消。
class MBroadcast extends Broadcast{
[@Override](https://my.oschina.net/u/1162528)
public void onReceive(Context context, Intent intent) {
Bundle bundle = RemoteInput.getResultsFromIntent(intent);
if (bundle != null){
String replayContent = bundle.getString("RemoteInputKey");
Log.d("MBroadcast", "onReceive: " + replayContent);
// 取消該通知
((NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE)).cancel(22);
}
}
}
給 Notification 添加一個 View 擴展視圖
在通知欄進行對後臺任務的控制的時候需要用到這個功能, 比如下載一個文件時可以 取消,暫停,後臺播放音樂時可以 切換暫停和顯示歌詞等。通知的自定義 view 最大高度爲 256 dp,且只有 Android N 以上版本才支持 自定義 view。
RemoteViews 只支持幾個 View,在這裏我使用 ConstraintLayout 作爲根 Layout 的時候發現會出現 android.app.RemoteServiceException: Bad notification posted from package xxx 這個錯誤,換了 LinearLayout 後就不會出現這個問題。支持的 View 如下
- Layout
- FrameLayout
- LinearLayout
- RelativeLayout
- GridLayout
- View Button,ImageView,ImageButton,TextView,ProgressBar,ListView,GridView,StackView,ViewStub,AdapterViewFlipper,ViewFlipper,AnalogClock,Chronometer
首先構建一個通知,創建一個 RemoteViews,RemoteViews 構造函數接受一個包含 view 所在的包名和一個 View 資源。RemoteViews 意思就是遠程 view,該 view 不存在與 當前 Activity,進程,通常用於通知中的自定義 view 和桌面小部件。
Notification.Builder builder = new Notification.Builder(this)
.setContentTitle("ContentTitle")
.setTicker("ThisIsTicker")
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentText("ContentText");
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.notify_view);
這裏的 notify_view 很簡單,如下
<LinearLayout ...>
<ImageView id="@+id/notify_iv"
.../>
<TextView id="@+id/notify_tv"
.../>
<Button id="@+id/notify_bt"
.../>
</LinearLayout>
設置 CustomContentView 和 Style。
builder.setCustomContentView(remoteViews);
Notification.DecoratedCustomViewStyle viewStyle = new Notification.DecoratedCustomViewStyle();
builder.setStyle(viewStyle);
如何監聽自定義 view RemoteViews 中的點擊事件,和更新 view
現在已經添加了一個 view,如何修改 view 中的數據以及響應 view 的事件呢?通過 RemoteViews.setOnClickPendingIntent(int r, PendingIntent intnet) 這個方法爲 一個 view 綁定一個 PendingIntent,當點擊了該view 後便執行相應的操作。
Intent intent = new Intent("NotificationAction");
intent.putExtra("action", "bt_click");
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 12, intent, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.notify_bt, pendingIntent);
更新 RemoteView 中的數據,更新一個 ImageView中的圖片,對各種 view 更新都有相應的方法。
remoteViews.setBitmap(R.id.notify_iv, "setImageBitmap", BitmapFactory.decodeResource(getResources(), R.drawable.me));