Android Notification 詳細使用 (學習筆記)

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

發送通知的步驟

  1. 獲取 NotificationManager
  2. 創建 Notification
  3. 給 Notification 設置參數
  4. 使用 NotificationManager 發送通知

Android 7.0 新內容

Notification.Builder.serPriority: 設置通知優先級 Notification.Builder.setStyle: 設置通知擴展布局 MessagingStyle: 快速回復

Android 8.0 新內容

NotificationChannel:用戶可以自定義通知的顯示,以及關閉某個通知的提示音震動等

構建一個最簡單的 Notification

一個簡單的通知包含,一個小圖標,一個標題和內容如圖:

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 不顯示任何內容

如何取消通知

  1. 用戶清除
  2. setAutoCancel(),點擊通知會清除
  3. NotificationManager.cancel(int id)
  4. NotificationManager.cancel(String tag, int id)
  5. 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 按鈕後將發出一條廣播

Action

超長內容的 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。

CustomView

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));

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章