概述
寫這篇文章的起因是當我自定義通知欄樣式時,設定的view高度最終只能顯示為通知欄的預設高度(64dp),讓人十分困惑,於是研究了下原始碼。
除了解開真相意外,還了解到了包括bigview,pengdingintent的點選事件,通知欄限制等知識點,收穫頗多,在這裡分享給大家。希望能讓大家明白一個通知是如何新增到狀態列上面去的,如果遇到奇怪的問題不至於抓瞎。
前提
本文是以android5.0的原始碼為基礎的並且假設大家都知道notification的基本用法。
構造通知
在我們使用NotificationCompat.Builder
物件設定完各種引數(小/大圖示,標題,內容等)後,最後會呼叫build
方法來得到一個Notification,然後使用NotificationManager
來發出通知。我們就先來看看build方法做了什麼事。
NotificationCompat是v4包中的一個類,做了android各個版本的相容,但是不論是哪個版本,最後build方法都是呼叫的Notification
的build方法,所以我們直接看Notification的build方法。
frameworks/base/core/java/android/app/Notification.java
/**
* Combine all of the options that have been set and return a new {@link Notification}
* object.
*/
public Notification build() {
...
//設定通知的預設資訊
Notification n = buildUnstyled();
//設定通知的樣式資訊
if (mStyle != null) {
n = mStyle.buildStyled(n);
}
...
return n;
}複製程式碼
方法的註釋說得很清楚了,就是將所有的設定選項聯合在一起返回一個新的通知。
buildUnstyled
其中buildUnstyled()所說的預設資訊就是在android4.0以前還不能展開的時候所包括的所有資訊,包括大/小圖示,時間,標題,內容,自定義view等資訊。
/**
* Apply the unstyled operations and return a new {@link Notification} object.
* @hide
*/
public Notification buildUnstyled() {
Notification n = new Notification();
n.when = mWhen;
n.icon = mSmallIcon;
...
setBuilderContentView(n, makeContentView());
n.contentIntent = mContentIntent;
...
setBuilderBigContentView(n, makeBigContentView());
setBuilderHeadsUpContentView(n, makeHeadsUpContentView());
// Note: If you`re adding new fields, also update restoreFromNotitification().
return n;
}複製程式碼
setBuilderContentView用於設定通知欄的ContentView屬性
private void setBuilderContentView(Notification n, RemoteViews contentView) {
n.contentView = contentView;
...
}複製程式碼
makeContentView()是構造出所需要填充的view
private RemoteViews makeContentView() {
if (mContentView != null) {
return mContentView;
} else {
return applyStandardTemplate(getBaseLayoutResource());
}
}複製程式碼
如果你沒有使用自定view,將會使用標準的模板樣式
private int getBaseLayoutResource() {
return R.layout.notification_template_material_base;
}複製程式碼
這裡只講了setBuilderContentView(n, makeContentView());
方法, 後面的setBuilderBigContentView(n, makeBigContentView());
和setBuilderHeadsUpContentView(n, makeHeadsUpContentView());
方法與其類似都是設定通知的相應屬性,直接給出結果,不再累述
setBuilderHeadsUpContentView --> n.headsUpContentView setBuilderBigContentView --> n.bigContentView = bigContentView;
這樣,通知的顯示內容就已經構造好了。
1 .可以看出,在構造階段,並沒有對通知欄的自定義view高度做出限制,但最後顯示的時候卻是一個固定高度,why?
2.先記住這裡的bigContentView屬性,後面會在提到。
buildStyled
4.0後如果設定了BigText,BigPic等樣式,則會呼叫buildStyled方法。buildStyled是Notification.Style中的一個方法
public Notification buildStyled(Notification wip) {
...
populateBigContentView(wip);
...
return wip;
}複製程式碼
populateBigContentView是一個protected所修飾的方法,具體的實現是在所設定的Style中,這裡裡BigPic Style為例,在BigPictureStyle
類中,重寫了該方法
@Override
public void populateBigContentView(Notification wip) {
mBuilder.setBuilderBigContentView(wip, makeBigContentView());
}複製程式碼
private RemoteViews makeBigContentView() {
RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
contentView.setImageViewBitmap(R.id.big_picture, mPicture);
...
return contentView;
}複製程式碼
private int getBigPictureLayoutResource() {
return R.layout.notification_template_material_big_picture;
}複製程式碼
如果是BigPic Style樣式的通知,其實也是呼叫了系統中設定的一個模板佈局notification_template_material_big_picture.
這裡又呼叫了一次mBuilder.setBuilderBigContentView
,前面提到了該方法是給notification的bigContentView的屬性賦值。所以如果設定了樣式,則會覆蓋預設的bigContentView值
總結
在通知構造環節,我們需要記住做了最重要的2件事
- 給Notification的contentView屬性賦值,該值可以是自定義的view也可以是系統預設樣式的view
- 給Notification的bigContentView屬性賦值,該值可以使自定義的view也可以是系統預設樣式的view
相關閱讀
Notification之—-Android5.0實現原理(二)
Notification之—-自定義樣式
Notification之—-預設樣式
Notification之—-任務棧