從零開(kāi)始--系統(tǒng)深入學(xué)習(xí)android
通知
一個(gè)通知是一條消息他是顯示于你應(yīng)用程序之外的一個(gè)界面中。當(dāng)你告訴系統(tǒng)要發(fā)布一個(gè)通知時(shí),它首先作為一個(gè)icon出現(xiàn)在通知區(qū)域。為了看見(jiàn)通知的細(xì)節(jié),用戶可以點(diǎn)擊通知區(qū)域展開(kāi)一個(gè)新的界面。
注 意:除非特別注明外,本章指的都是NotificationCompat.Builder,它在v4 Support Library中有,正式添加于API Level 15。但有了v4 Support Library低版本系統(tǒng)也能用。另外Notification.Builder添加于android3.0。
7.1 通知顯示元素
通知有兩種可視化風(fēng)格,它取決于版本和drawer的狀態(tài):
標(biāo)準(zhǔn)view
在drawer中標(biāo)準(zhǔn)的通知view
大型view
一個(gè)比較大的view。這個(gè)view是通知擴(kuò)展的一個(gè)部分,這個(gè)功能被添加于android 4.1。
7.1.1正常view
一個(gè)標(biāo)準(zhǔn)view一般高位64dp。即使你創(chuàng)建一個(gè)大的view風(fēng)格,直到它展開(kāi)之前他仍然出現(xiàn)在標(biāo)準(zhǔn)view中。
以下是每個(gè)部分的說(shuō)明:
1. 內(nèi)容標(biāo)題
2. 大型icon
3. 內(nèi)容text
4. 內(nèi)容info
5. 小型icon
6. 發(fā)布通知的時(shí)間。你能使用setWhen()設(shè)置一個(gè)明確的值。
7.1.2大型view
當(dāng)通知被展開(kāi)時(shí),大型view才會(huì)出現(xiàn)并顯示,一般由用戶使用展開(kāi)手勢(shì),通知drawer會(huì)被展開(kāi)。展開(kāi)的通知在android 4.1上才可用。如圖7-4所示:
注意大型View大部分的視覺(jué)元素與正常的視圖共享。僅僅不同的地方是編號(hào)為數(shù)字7的地方,這個(gè)細(xì)節(jié)區(qū)域。各大View風(fēng)格設(shè)置有些不同。可用的風(fēng)格有:
Big picture style
細(xì)節(jié)區(qū)域包含一個(gè)256dp高度的bitmap在它的細(xì)節(jié)部分。
Big text style
在細(xì)節(jié)部分顯示一個(gè)大型文本塊。
Inbox style
在細(xì)節(jié)部分顯示文本行數(shù)。
下面是大型view可用,但標(biāo)準(zhǔn)view不可用的風(fēng)格:
Big content title
允許你覆蓋標(biāo)準(zhǔn)view的內(nèi)容標(biāo)題,使之出現(xiàn)在展開(kāi)view中
Summary text
允許你在細(xì)節(jié)區(qū)域添加文本行數(shù)。
7.2 創(chuàng)建一個(gè)通知
你 想在NotificationCompat.Builder對(duì)象中為通知指定UI信息和動(dòng)作,就必須先使用 NotificationCompat.Builder.build()來(lái)創(chuàng)建通知,這個(gè)方法會(huì)返回一個(gè)Notification對(duì)象,為了發(fā)布通知,你 可以通過(guò)調(diào)用NotificationManager.notify()來(lái)傳遞Notification對(duì)象到系統(tǒng)中。
7.2.1必須的通知內(nèi)容
一個(gè)Notification對(duì)象必須包含以下內(nèi)容:
通過(guò)setSmallIcon()設(shè)置一個(gè)小的icon
通過(guò)setContentTitle()來(lái)設(shè)置一個(gè)標(biāo)題
通過(guò)setContentText()來(lái)設(shè)置細(xì)節(jié)文本
7.2.2可選的通知的內(nèi)容和設(shè)置
所有其他通知設(shè)置和內(nèi)容都是可選的,具體可參考API NotificationCompat.Builder類
7.2.3通知?jiǎng)幼鳎╝ction)
雖 然它們是可選的,你應(yīng)該至少添加一個(gè)動(dòng)作到你的通知中。一個(gè)動(dòng)作允許用戶直接從通知到一個(gè)你應(yīng)用程序的Activity中。一個(gè)通知能提供多個(gè)動(dòng)作。你應(yīng) 該總是定義一個(gè)動(dòng)作,當(dāng)用戶點(diǎn)擊通知時(shí),觸發(fā)它。通常這個(gè)動(dòng)作打開(kāi)一個(gè)你應(yīng)用程序中的Activity。你也能添加按鈕到通知中(Android 4.1中加入的新功能),用來(lái)執(zhí)行額外的動(dòng)作,如一個(gè)警告或即時(shí)響應(yīng)的文本消息。如果你使用附加的動(dòng)作按鈕,你必須讓他們的功能在一個(gè)Activity中 可用。在通知里面,這個(gè)動(dòng)作通過(guò)PendingIntent來(lái)定義,請(qǐng)使用NotificationCompat.Builder中合適的方法來(lái)創(chuàng)建。例 如,當(dāng)用戶在Drawer中點(diǎn)擊通知文本的時(shí)候,你想要啟動(dòng)Activity,你就可以通過(guò)調(diào)用setContentIntent()來(lái)添加一個(gè) PendingIntent。用戶點(diǎn)擊通知啟動(dòng)一個(gè)Activity是最常見(jiàn)的情況。請(qǐng)記住在Androird4.1或更高版本中,你才能從一個(gè)動(dòng)作按鈕 中啟動(dòng)一個(gè)Activity。
7.2.4 創(chuàng)建一個(gè)簡(jiǎn)單的通知
以下代碼片段是一個(gè)簡(jiǎn)單的例子,當(dāng)一個(gè)用戶點(diǎn)擊通知時(shí)會(huì)打開(kāi)一個(gè)activity。注意此段代碼創(chuàng)建一個(gè)TaskStackBuilder對(duì)象并使用它創(chuàng)建一個(gè)PendingIntent。
- otificationCompat.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.2.5 應(yīng)用一個(gè)大型的view風(fēng)格到通知中
當(dāng) 一個(gè)通+知展開(kāi)后出現(xiàn)一個(gè)大型的view,首先創(chuàng)建一個(gè)你想要的NotificationCompat.Builder對(duì)象。然后調(diào)用 Builder.setStyle()傳入大型view的風(fēng)格對(duì)象。記住android4.1之前的版本是不可用的。當(dāng)然后面我們會(huì)講解如何兼容低版本。
- 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.6 處理兼容性
并 不是所有通知功能都用于特別的版本。例如動(dòng)作按鈕,依賴于展開(kāi)的通知,它只出現(xiàn)于android 4.1和高版本中。因?yàn)橹挥性谶@個(gè)版本上展開(kāi)的通知才可用。為了確保***的兼容性,請(qǐng)使用NotificationCompat及其子類創(chuàng)建通知,***是 用NotificationCompat.Builder,此外當(dāng)你實(shí)現(xiàn)一個(gè)通知,請(qǐng)遵循以下過(guò)程:
1. 不管用戶使用什么系統(tǒng)版本,都應(yīng)該提供通知所有的功能給所有用戶,為了做到這一點(diǎn),需要在一個(gè)activity中驗(yàn)證所有的功能可用。你可能想要添加一個(gè) 新的Activity。例如,如果您想要使用addAction()來(lái)控制停止和啟動(dòng)媒體播放,首先需要在一個(gè)Activity中實(shí)現(xiàn)這個(gè)控制。
2. 當(dāng)用戶點(diǎn)擊通知時(shí),確保所有用戶點(diǎn)擊后都能啟動(dòng)一個(gè)界面。我們需要為Activity創(chuàng)建一個(gè)PendingIntent。然后使用setContentIntent()把PendingIntent添加到通知中。
7.3 管理通知
當(dāng) 你需要為同一類型的事件多次處理一個(gè)通知時(shí),你應(yīng)該避免每次重新生成新的通知。你應(yīng)該考慮更新先前的通知,不是改變一些值就是添加一些值。例 如,Gmail通知用戶新的email已經(jīng)收到了,并且未讀消息會(huì)自增計(jì)數(shù),其實(shí)就是沒(méi)收到一個(gè)通知消息做了處理。這就是所謂的“堆疊”通知。
7.3.1 更新通知
通 知當(dāng)然是可以被更新,使用通知ID來(lái)更新它,調(diào)用NotificationManager.notify(ID, notification)即可。如果先前的通知仍然可見(jiàn),系統(tǒng)會(huì)從Notification 對(duì)象的content中更新它。如果先前的通知已經(jīng) dismiss掉了,一個(gè)新的通知將被創(chuàng)建。下面的代碼演示了一個(gè)通知更新并顯示事件數(shù)量,
- mNotificationManager =
- (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);//被更新的通知IDint 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); // 因?yàn)镮D保持不變,存在的通知被更新 mNotificationManager.notify(
- notifyID,
- mNotifyBuilder.build());
- ...
7.3.2 移除通知
移除通知的方法如下:
1. 用戶通過(guò)個(gè)人或“Clear All” dismiss通知
2. 用戶點(diǎn)擊通知并且你在創(chuàng)建通知的時(shí)候調(diào)用了setAutoCancel()方法
3. 你根據(jù)指定的通知ID調(diào)用cancel()方法。這個(gè)方法也刪除正在進(jìn)行中的通知
4. 你調(diào)用cancelAll(),它將刪除你先前所有的通知
7.4 當(dāng)啟動(dòng)一個(gè)Activity時(shí)保持導(dǎo)航
當(dāng) 你從一個(gè)通知中啟動(dòng)一個(gè)Activity的時(shí)候,你必須保存用戶的語(yǔ)氣導(dǎo)航體驗(yàn)。點(diǎn)擊返回鍵將和點(diǎn)擊Home鍵的效果一樣。為了保存導(dǎo)航的用戶體驗(yàn),你應(yīng) 該在一個(gè)新的任務(wù)中啟動(dòng)Activity。根據(jù)給定的新任務(wù)如何設(shè)置PendingIntent取決于你啟動(dòng)的Activity的性質(zhì)。通常有兩種情況:
1. 常規(guī)activity
正 常啟動(dòng)Activity的情況下,設(shè)置PendingIntent來(lái)啟動(dòng)一個(gè)新的任務(wù),并給PendingIntent提供一個(gè)后臺(tái)堆棧,以復(fù)制應(yīng)用程序 正常點(diǎn)擊Back鍵的行為。從Gmail APP中來(lái)演示這個(gè)通知。當(dāng)出現(xiàn)一條信息時(shí)你點(diǎn)擊通知,就會(huì)看到信息本身。當(dāng)你點(diǎn)擊Back鍵時(shí)你會(huì)通過(guò)Gmail回退到Home屏幕,就像你已經(jīng)進(jìn)入了 Gmail,從Gmail退到Home屏幕一樣。
2. 特別的activity
這 種Activity,是在某種意義上Activity擴(kuò)展了通知提供的信息,因?yàn)楹茈y顯示在通知本身。在這種情況下,設(shè)置PendingIntent啟動(dòng) 一個(gè)新的任務(wù)。沒(méi)有必要?jiǎng)?chuàng)建一個(gè)后臺(tái)堆棧,因?yàn)檫@個(gè)Activity并不是屬于應(yīng)用程序正流程下的一部分。點(diǎn)擊Back鍵后依然會(huì)顯示Home屏幕。
7.4.1 通過(guò)PendingIntent設(shè)立一個(gè)規(guī)則的activity
1. 在manifest中定義你應(yīng)用程序的Activity層級(jí)。
◆支持Android 4.0.3和更早期的版本。在<activity>節(jié)點(diǎn)下添加子節(jié)點(diǎn)<meta-data>。表示父Activity與子Activity的關(guān)系。在<meta-data>節(jié)點(diǎn)下設(shè)置
android:name="android.support.PARENT_ACTIVITY"
android:value="<parent_activity_name>"
◆支持Android4.1和更高的版本。在<activity>節(jié)點(diǎn)中添加android:parentActivityName屬性
***支持的xml代碼
- <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><activityandroid:name=".ResultActivity"<!—android 4.1和更高版本 -->android:parentActivityName=".MainActivity"><!—android 4.0.3和更早版本 -->
- <meta-data
- android:name="android.support.PARENT_ACTIVITY"
- android:value=".MainActivity"/></activity>
2. 創(chuàng)建一個(gè)后臺(tái)推?;贗ntent啟動(dòng)的Activity:
◆創(chuàng)建Intent來(lái)啟動(dòng)Activity
◆通過(guò)調(diào)用TaskStackBuilder.create()來(lái)創(chuàng)建一個(gè)任務(wù)棧
◆ 通過(guò)調(diào)用addParentStack()把后臺(tái)推棧添加到棧中。對(duì)于你在manifest中定義的每一個(gè)Activity層級(jí),后臺(tái)堆棧都包含一個(gè)啟動(dòng) Activity的Intent對(duì)象。并且這個(gè)方法還添加了標(biāo)記(flag)來(lái)啟動(dòng)新任務(wù)中的堆棧。注意:盡管addParentStack()的參數(shù)是 一個(gè)啟動(dòng)Activity的引用,但這個(gè)方法實(shí)際并沒(méi)有添加Intent進(jìn)去。
◆通過(guò)調(diào)用addNextIntent()來(lái)添加Intent對(duì)象。它添加的對(duì)象就是最上面◆創(chuàng)建的那個(gè)intent對(duì)象。
◆如果你需要在堆棧上添加參數(shù)到Intent對(duì)象你可以調(diào)用TaskStackBuilder.editIntentAt()。這有時(shí)候是必要的, 當(dāng)用戶使用back鍵導(dǎo)航回來(lái),以確保目標(biāo)Activity顯示有意義的數(shù)據(jù)。
◆調(diào)用getPendingIntent()獲得一個(gè)PendingIntent。然后你能調(diào)用setContentIntent()把這個(gè)PendingIntent作為參數(shù)。
下面的代碼片段演示了這個(gè)過(guò)程
- ...
- Intent resultIntent = new Intent(this, ResultActivity.class);
- TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);// 添加后臺(tái)堆棧stackBuilder.addParentStack(ResultActivity.class);// 添加Intent到棧頂stackBuilder.addNextIntent(resultIntent);// 獲得一個(gè)PendingIntent包含整個(gè)后臺(tái)堆棧 containing the entire back stackPendingIntent 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.4.2 通過(guò)PendingIntent設(shè)立一個(gè)專用的activity
以下部分描述了怎樣通過(guò)PendingIntent設(shè)置一個(gè)專用的activity。
一 個(gè)專用的Activity 不需要后臺(tái)棧,因此你不一定要在manifest中定義它的Activity層級(jí),并且你不必調(diào)用addParentStack() 來(lái)構(gòu)建一個(gè)后臺(tái)棧。 反而使用manifest設(shè)置Activity的任務(wù)選項(xiàng),并通過(guò)調(diào)用getActivity()創(chuàng)建PendingIntent:
1. 在manifest中添加以下屬性到<activity>節(jié)點(diǎn)中。
◆android:name="activityclass"
完整的類名
◆android:taskAffinity=""
于代碼中設(shè)置的FLAG_ACTIVITY_NEW_TASK 標(biāo)記相結(jié)合,它確保這個(gè)activity不能進(jìn)入應(yīng)用程序的默認(rèn)任務(wù)中。
◆android:excludeFromRecents="true"
從Recents排除了新的任務(wù),以便用戶不會(huì)意外導(dǎo)航回它。
- <activity android:name=".ResultActivity"...
- android:launchMode="singleTask"
- android:taskAffinity=""
- android:excludeFromRecents="true"></activity>...
2. 構(gòu)建和發(fā)布通知
◆創(chuàng)建一個(gè)Intent來(lái)啟動(dòng)Activity
◆設(shè)置Activity啟動(dòng)在一個(gè)新的、空的任務(wù)中,通過(guò)setFlags()方法來(lái)處理,傳入FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TASK
◆為Intent設(shè)置其他需要的選項(xiàng)
◆通過(guò)getActivity()從Intent中創(chuàng)建一個(gè)PendingIntent。你能使用這個(gè)PendingIntent對(duì)象,把他作為參數(shù)傳到setContentIntent()中
- 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.5 顯示通知的進(jìn)度
通知可以包括一個(gè)動(dòng)畫(huà)進(jìn)度指示器顯示用戶正在運(yùn)行的操作的狀態(tài)。如果你能估計(jì)這種操作需要花費(fèi)多長(zhǎng)時(shí)間,可以使用"determinate"形式的指示器(一個(gè)progress bar)。如果你不能估計(jì)花費(fèi)的時(shí)間,使用“indeterminate”形式的指示器。
7.5.1 顯示一個(gè)固定的時(shí)間進(jìn)度指示器
顯示一個(gè)確定的進(jìn)度條, 通過(guò)調(diào)用setProgress()添加bar到你的通知中,setProgress(max, progress, false),然后發(fā)出通知。
- ...
- 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.5.2顯示一個(gè)持續(xù)的Activity指示器
為 了顯示不確定的activity指示器,使用setProgress(0, 0, true)添加它到你的通知中,前面兩個(gè)參數(shù)可以忽略,然后發(fā)出通知。它的結(jié)果是一個(gè)指示器,具有和進(jìn)度條相同的樣式,不同的地方就是它的動(dòng)畫(huà)是持續(xù)的。 在操作開(kāi)始的地方就發(fā)出通知,這個(gè)動(dòng)畫(huà)將一直執(zhí)行,直到你修改通知為止。將操作完成時(shí),你應(yīng)該手動(dòng)調(diào)用setProgress(0, 0, false)然后更新通知移除activity指示器。如果你不這么做就算操作完成動(dòng)畫(huà)也會(huì)繼續(xù)運(yùn)行。
- mBuilder.setProgress(100, incr, false);
- mNotifyManager.notify(0, mBuilder.build());
7.6 自定義通知布局
通 知框架允許你自定義通知布局,它在一個(gè)RemoteViews 對(duì)象中定義了通知的外觀。自定義布局通知和正常的通知類似,它們都是基于一個(gè) RemoteViews定義在一個(gè)XML布局文件。自定義通知的可用高度取決于通知view的布局。正常view布局限制為64dp,展開(kāi)view布局限 制為256dp。自定義通知布局,通過(guò)實(shí)例化一個(gè)RemoteViews對(duì)象然后inflates一個(gè)xml布局文件啟動(dòng)。不再調(diào)用 setContentTitle()方法,而使用setContent()方法來(lái)設(shè)置自定義通知的內(nèi)容細(xì)節(jié)。使用這個(gè)方法在RemoteViews中來(lái)設(shè) 置view子類的值:
1. 為通知?jiǎng)?chuàng)建一個(gè)單獨(dú)的xml布局文件。
2. 在你的應(yīng)用程序中,使用RemoteViews方法來(lái)定義你通知的icon和文本。調(diào)用setContent()方法put這個(gè)RemoteViews對(duì) 象到你的NotificationCompat.Builder中。避免正在RemoteViews對(duì)象中設(shè)置Drawable背景,因?yàn)槟愕奈谋绢伾?能會(huì)變的看不清。
RemoteViews類也包括早期Chronometer或ProgressBar中的方法 ,更多詳細(xì)信息請(qǐng)參考API文檔。
當(dāng)你使用自定義通知布局時(shí),請(qǐng)?zhí)貏e注意不同的設(shè)備分辨率和水平方向上的問(wèn)題。不要讓你的自定義布局太復(fù)雜,一定要在各種配置中測(cè)試。
本文鏈接:http://my.oschina.net/u/947024/blog/293699