從零開始--系統深入學習android(實踐-讓我們開始寫代碼-Android框架學習-7.通知)


第7章 通知

一個通知是一條消息他是顯示於你應用程序之外的一個界面中。當你告訴系統要發布一個通知時,它首先作為一個icon出現在通知區域。為了看見通知的細節,用戶可以點擊通知區域展開一個新的界面。下面讓我們來看一下圖7-1和圖7-2:

 

圖7-1 通知出現在通知區域

 

圖7-2 通知展開后的效果(drawer)

注意:除非特別注明外,本章指的都是NotificationCompat.Builder,它在v4 Support Library中有,正式添加於API Level 15。但有了v4 Support Library低版本系統也能用。另外Notification.Builder添加於android3.0。

7.1 通知顯示元素

通知有兩種可視化風格,它取決於版本和drawer的狀態:

標准view

在drawer中標准的通知view

大型view

一個比較大的view。這個view是通知擴展的一個部分,這個功能被添加於android 4.1。

7.1.1正常view

一個標准view一般高位64dp。即使你創建一個大的view風格,直到它展開之前他仍然出現在標准view中。如圖7-3:

 

圖7-3 在標准view下的通知

以下是每個部分的說明:

1. 內容標題

2. 大型icon

3. 內容text

4. 內容info

5. 小型icon

6. 發布通知的時間。你能使用setWhen()設置一個明確的值。

7.1.2大型view

當通知被展開時,大型view才會出現並顯示,一般由用戶使用展開手勢,通知drawer會被展開。展開的通知在android 4.1上才可用。如圖7-4所示:

 

圖7-4 在大型view下的通知

注意大型View大部分的視覺元素與正常的視圖共享。僅僅不同的地方是編號為數字7的地方,這個細節區域。各大View風格設置有些不同。可用的風格有:

Big picture style

細節區域包含一個256dp高度的bitmap在它的細節部分。

Big text style

在細節部分顯示一個大型文本塊。

Inbox style

在細節部分顯示文本行數。

下面是大型view可用,但標准view不可用的風格:

Big content title

允許你覆蓋標准view的內容標題,使之出現在展開view中

Summary text

允許你在細節區域添加文本行數。

7.2 創建一個通知

你想在NotificationCompat.Builder對象中為通知指定UI信息和動作,就必須先使用NotificationCompat.Builder.build()來創建通知,這個方法會返回一個Notification對象,為了發布通知,你可以通過調用NotificationManager.notify()來傳遞Notification對象到系統中。

7.2.1必須的通知內容

一個Notification對象必須包含以下內容:

通過setSmallIcon()設置一個小的icon

通過setContentTitle()來設置一個標題

通過setContentText()來設置細節文本

7.2.2可選的通知的內容和設置

所有其他通知設置和內容都是可選的,具體可參考API NotificationCompat.Builder類

7.2.3通知動作(action)

雖然它們是可選的,你應該至少添加一個動作到你的通知中。一個動作允許用戶直接從通知到一個你應用程序的Activity中。一個通知能提供多個動作。你應該總是定義一個動作,當用戶點擊通知時,觸發它。通常這個動作打開一個你應用程序中的Activity。你也能添加按鈕到通知中(Android 4.1中加入的新功能),用來執行額外的動作,如一個警告或即時響應的文本消息。如果你使用附加的動作按鈕,你必須讓他們的功能在一個Activity中可用。在通知里面,這個動作通過PendingIntent來定義,請使用NotificationCompat.Builder中合適的方法來創建。例如,當用戶在Drawer中點擊通知文本的時候,你想要啟動Activity,你就可以通過調用setContentIntent()來添加一個PendingIntent。用戶點擊通知啟動一個Activity是最常見的情況。請記住在Androird4.1或更高版本中,你才能從一個動作按鈕中啟動一個Activity。

7.2.4 創建一個簡單的通知

以下代碼片段是一個簡單的例子,當一個用戶點擊通知時會打開一個activity。注意此段代碼創建一個TaskStackBuilder對象並使用它創建一個PendingIntent。如代碼清單7-1所示:

NotificationCompat.Builder mBuilder =
        new NotificationCompat.Builder(this)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!");
Intent resultIntent = new Intent(this, ResultActivity.class);
 
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(ResultActivity.class);
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent =
        stackBuilder.getPendingIntent(
            0,
            PendingIntent.FLAG_UPDATE_CURRENT
        );
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager =
    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(mId, mBuilder.build());

 

代碼清單 7-1

7.2.5 應用一個大型的view風格到通知中

當一個通+知展開后出現一個大型的view,首先創建一個你想要的NotificationCompat.Builder對象。然后調用Builder.setStyle()傳入大型view的風格對象。記住android4.1之前的版本是不可用的。當然后面我們會講解如何兼容低版本。下面代碼清單7-2是在7-1上做了適當修改:

NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
    .setSmallIcon(R.drawable.notification_icon)
    .setContentTitle("Event tracker")
    .setContentText("Events received")
NotificationCompat.InboxStyle inboxStyle =
        new NotificationCompat.InboxStyle();
String[] events = new String[6];
inboxStyle.SetBigContentTitle("Event tracker details:");
...
for (int i=0; i < events.length; i++) {
 
    inboxStyle.addLine(events[i]);
}
mBuilder.setStyle(inBoxStyle);
...

 

代碼清單 7-2

7.2.6 處理兼容性

並不是所有通知功能都用於特別的版本。例如動作按鈕,依賴於展開的通知,它只出現於android 4.1和高版本中。因為只有在這個版本上展開的通知才可用。為了確保最好的兼容性,請使用NotificationCompat及其子類創建通知,最好是用NotificationCompat.Builder,此外當你實現一個通知,請遵循以下過程:

1. 不管用戶使用什么系統版本,都應該提供通知所有的功能給所有用戶,為了做到這一點,需要在一個activity中驗證所有的功能可用。你可能想要添加一個新的Activity。例如,如果您想要使用addAction()來控制停止和啟動媒體播放,首先需要在一個Activity中實現這個控制。

2. 當用戶點擊通知時,確保所有用戶點擊后都能啟動一個界面。我們需要為Activity創建一個PendingIntent。然后使用setContentIntent()把PendingIntent添加到通知中。

7.3 管理通知

當你需要為同一類型的事件多次處理一個通知時,你應該避免每次重新生成新的通知。你應該考慮更新先前的通知,不是改變一些值就是添加一些值。例如,Gmail通知用戶新的email已經收到了,並且未讀消息會自增計數,其實就是沒收到一個通知消息做了處理。這就是所謂的“堆疊”通知。

7.3.1 更新通知

通知當然是可以被更新,使用通知ID來更新它,調用NotificationManager.notify(ID, notification)即可。如果先前的通知仍然可見,系統會從Notification 對象的content中更新它。如果先前的通知已經dismiss掉了,一個新的通知將被創建。下面的代碼演示了一個通知更新並顯示事件數量,如代碼清單7-3所示:

 
mNotificationManager =
        (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
//被更新的通知ID
int notifyID = 1;
mNotifyBuilder = new NotificationCompat.Builder(this)
    .setContentTitle("New Message")
    .setContentText("You've received new messages.")
    .setSmallIcon(R.drawable.ic_notify_status)
numMessages = 0;
...
    mNotifyBuilder.setContentText(currentText)
        .setNumber(++numMessages);
    // 因為ID保持不變,存在的通知被更新
    mNotificationManager.notify(
            notifyID,
            mNotifyBuilder.build());
...

 

代碼清單 7-3

上面那些代碼運行后就會生成如圖7-5所示的效果:

 

圖7-5 在drawer中顯示更新通知

7.3.2 移除通知

移除通知的方法如下:

1. 用戶通過個人或“Clear All” dismiss通知

2. 用戶點擊通知並且你在創建通知的時候調用了setAutoCancel()方法

3. 你根據指定的通知ID調用cancel()方法。這個方法也刪除正在進行中的通知

4. 你調用cancelAll(),它將刪除你先前所有的通知

7.4 當啟動一個Activity時保持導航

當你從一個通知中啟動一個Activity的時候,你必須保存用戶的語氣導航體驗。點擊返回鍵將和點擊Home鍵的效果一樣。為了保存導航的用戶體驗,你應該在一個新的任務中啟動Activity。根據給定的新任務如何設置PendingIntent取決於你啟動的Activity的性質。通常有兩種情況:

1. 常規activity

正常啟動Activity的情況下,設置PendingIntent來啟動一個新的任務,並給PendingIntent提供一個后台堆棧,以復制應用程序正常點擊Back鍵的行為。從Gmail APP中來演示這個通知。當出現一條信息時你點擊通知,就會看到信息本身。當你點擊Back鍵時你會通過Gmail回退到Home屏幕,就像你已經進入了Gmail,從Gmail退到Home屏幕一樣。

2. 特別的activity

這種Activity,是在某種意義上Activity擴展了通知提供的信息,因為很難顯示在通知本身。在這種情況下,設置PendingIntent啟動一個新的任務。沒有必要創建一個后台堆棧,因為這個Activity並不是屬於應用程序正常流程下的一部分。點擊Back鍵后依然會顯示Home屏幕。

7.4.1 通過PendingIntent設立一個規則的activity

1. 在manifest中定義你應用程序的Activity層級。

◆支持Android 4.0.3和更早期的版本。在<activity>節點下添加子節點<meta-data>。表示父Activity與子Activity的關系。在<meta-data>節點下設置

android:name="android.support.PARENT_ACTIVITY"

android:value="<parent_activity_name>"

◆支持Android4.1和更高的版本。在<activity>節點中添加android:parentActivityName屬性

完美支持的xml代碼如代碼清單7-4所示:

<activity

    android:name=".MainActivity"

    android:label="@string/app_name" >

    <intent-filter>

        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />

    </intent-filter>

</activity>

<activity

android:name=".ResultActivity"

<!—android 4.1和更高版本 -->

android:parentActivityName=".MainActivity">

<!—android 4.0.3和更早版本 -->

    <meta-data 

        android:name="android.support.PARENT_ACTIVITY"

        android:value=".MainActivity"/>

</activity>

 

代碼清單 7-4

2. 創建一個后台推棧基於Intent啟動的Activity:

◆創建Intent來啟動Activity

◆通過調用TaskStackBuilder.create()來創建一個任務棧

◆通過調用addParentStack()把后台推棧添加到棧中。對於你在manifest中定義的每一個Activity層級,后台堆棧都包含一個啟動Activity的Intent對象。並且這個方法還添加了標記(flag)來啟動新任務中的堆棧。注意:盡管addParentStack()的參數是一個啟動Activity的引用,但這個方法實際並沒有添加Intent進去。

◆通過調用addNextIntent()來添加Intent對象。它添加的對象就是最上面◆創建的那個intent對象。

◆如果你需要在堆棧上添加參數到Intent對象你可以調用TaskStackBuilder.editIntentAt()。這有時候是必要的, 當用戶使用back鍵導航回來,以確保目標Activity顯示有意義的數據。

◆調用getPendingIntent()獲得一個PendingIntent。然后你能調用setContentIntent()把這個PendingIntent作為參數。

下面的代碼片段演示了這個過程,如代碼清單7-5所示:

...
Intent resultIntent = new Intent(this, ResultActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
// 添加后台堆棧
stackBuilder.addParentStack(ResultActivity.class);
// 添加Intent到棧頂
stackBuilder.addNextIntent(resultIntent);
// 獲得一個PendingIntent包含整個后台堆棧 containing the entire back stack
PendingIntent resultPendingIntent =
        stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
...
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager =
    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(id, builder.build());

 

代碼清單 7-5

7.4.2 通過PendingIntent設立一個專用的activity

以下部分描述了怎樣通過PendingIntent設置一個專用的activity。

一個專用的Activity 不需要后台棧,因此你不一定要在manifest中定義它的Activity層級,並且你不必調用addParentStack() 來構建一個后台棧。反而使用manifest設置Activity的任務選項,並通過調用getActivity()創建PendingIntent: 

1. 在manifest中添加以下屬性到<activity>節點中。

◆android:name="activityclass"

完整的類名

◆android:taskAffinity=""

於代碼中設置的FLAG_ACTIVITY_NEW_TASK 標記相結合,它確保這個activity不能進入應用程序的默認任務中。

◆android:excludeFromRecents="true"

從Recents排除了新的任務,以便用戶不會意外導航回它。

如代碼清單7-6所示:

<activity
    android:name=".ResultActivity"
...
    android:launchMode="singleTask"
    android:taskAffinity=""
    android:excludeFromRecents="true">
</activity>
...

 

代碼清單 7-6

2. 構建和發布通知

◆創建一個Intent來啟動Activity

◆設置Activity啟動在一個新的、空的任務中,通過setFlags()方法來處理,傳入FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TASK

◆為Intent設置其他需要的選項

◆通過getActivity()從Intent中創建一個PendingIntent。你能使用這個PendingIntent對象,把他作為參數傳到setContentIntent()中

如代碼清單7-7所示:

NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
Intent notifyIntent =
        new Intent(new ComponentName(this, ResultActivity.class));
notifyIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent notifyIntent =
        PendingIntent.getActivity(
        this,
        0,
        notifyIntent
        PendingIntent.FLAG_UPDATE_CURRENT
);
 
builder.setContentIntent(notifyIntent);
NotificationManager mNotificationManager =
    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(id, builder.build());

 

代碼清單 7-7

7.5 顯示通知的進度

通知可以包括一個動畫進度指示器顯示用戶正在運行的操作的狀態。如果你能估計這種操作需要花費多長時間,可以使用"determinate"形式的指示器(一個progress bar)。如果你不能估計花費的時間,使用“indeterminate”形式的指示器。

7.5.1 顯示一個固定的時間進度指示器

顯示一個確定的進度條, 通過調用setProgress()添加bar到你的通知中,setProgress(max, progress, false),然后發出通知。如代碼清單7-8所示:

...
mNotifyManager =
        (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mBuilder = new NotificationCompat.Builder(this);
mBuilder.setContentTitle("Picture Download")
    .setContentText("Download in progress")
    .setSmallIcon(R.drawable.ic_notification);
new Thread(
    new Runnable() {
        @Override
        public void run() {
            int incr;
           
            for (incr = 0; incr <= 100; incr+=5) {
                   
                    mBuilder.setProgress(100, incr, false);
                    
                    mNotifyManager.notify(0, mBuilder.build());
                        
                        try {
                            Thread.sleep(5*1000);
                        } catch (InterruptedException e) {
                            Log.d(TAG, "sleep failure");
                        }
            }
            
            mBuilder.setContentText("Download complete")
            
                    .setProgress(0,0,false);
            mNotifyManager.notify(ID, mBuilder.build());
        }
    }
).start();

 

代碼清單 7-8

圖7-6則是運行后的效果

 

圖7-6 通知中progress bar運行的效果

7.5.2顯示一個持續的Activity指示器

為了顯示不確定的activity指示器,使用setProgress(0, 0, true)添加它到你的通知中,前面兩個參數可以忽略,然后發出通知。它的結果是一個指示器,具有和進度條相同的樣式,不同的地方就是它的動畫是持續的。在操作開始的地方就發出通知,這個動畫將一直執行,直到你修改通知為止。將操作完成時,你應該手動調用setProgress(0, 0, false)然后更新通知移除activity指示器。如果你不這么做就算操作完成動畫也會繼續運行。下面讓我們看下代碼清單7-9:

mBuilder.setProgress(100, incr, false);
mNotifyManager.notify(0, mBuilder.build());

 

代碼清單 7-9

運行后的效果如圖7-7所示:

 

圖7-7 持續的activity指示器動畫

7.6 自定義通知布局

通知框架允許你自定義通知布局,它在一個RemoteViews 對象中定義了通知的外觀。自定義布局通知和正常的通知類似,它們都是基於一個RemoteViews定義在一個XML布局文件。自定義通知的可用高度取決於通知view的布局。正常view布局限制為64dp,展開view布局限制為256dp。自定義通知布局,通過實例化一個RemoteViews對象然后inflates一個xml布局文件啟動。不再調用setContentTitle()方法,而使用setContent()方法來設置自定義通知的內容細節。使用這個方法在RemoteViews中來設置view子類的值:

1. 為通知創建一個單獨的xml布局文件。

2. 在你的應用程序中,使用RemoteViews方法來定義你通知的icon和文本。調用setContent()方法put這個RemoteViews對象到你的NotificationCompat.Builder中。避免正在RemoteViews對象中設置Drawable背景,因為你的文本顏色可能會變的看不清。

RemoteViews類也包括早期Chronometer或ProgressBar中的方法 ,更多詳細信息請參考API文檔。

當你使用自定義通知布局時,請特別注意不同的設備分辨率和水平方向上的問題。不要讓你的自定義布局太復雜,一定要在各種配置中測試。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM