Android Notification 使用
如何創建通知
隨著Android系統不斷升級,Notification的創建方式也隨之變化,主要變化如下:
Android 3.0之前
Android 3.0 (API level 11)之前,使用new Notification()方式創建通知:
NotificationManager mNotifyMgr =
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
PendingIntent contentIntent = PendingIntent.getActivity(
this, 0, new Intent(this, ResultActivity.class), 0);
Notification notification = new Notification(icon, tickerText, when);
notification.setLatestEventInfo(this, title, content, contentIntent);
mNotifyMgr.notify(NOTIFICATIONS_ID, notification);</code></pre>
Android 3.0 (API level 11)及更高版本
Android 3.0開始棄用new Notification()方式,改用Notification.Builder()來創建通知:
NotificationManager mNotifyMgr =
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
PendingIntent contentIntent = PendingIntent.getActivity(
this, 0, new Intent(this, ResultActivity.class), 0);
Notification notification = new Notification.Builder(this)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle("My notification")
.setContentText("Hello World!")
.setContentIntent(contentIntent)
.build();// getNotification()
mNotifyMgr.notify(NOTIFICATIONS_ID, notification);</code></pre>
這里需要注意:
"build()" 是Androdi 4.1(API level 16)加入的,用以替代
"getNotification()"。API level 16開始棄用"getNotification()"
兼容Android 3.0之前的版本
為了兼容API level 11之前的版本,v4 Support Library中提供了
NotificationCompat.Builder()這個替代方法。它與Notification.Builder()類似,二者沒有太大區別。
NotificationManager mNotifyMgr =
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
PendingIntent contentIntent = PendingIntent.getActivity(
this, 0, new Intent(this, ResultActivity.class), 0);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle("My notification")
.setContentText("Hello World!")
.setContentIntent(contentIntent);
mNotifyMgr.notify(NOTIFICATIONS_ID, mBuilder.build());</code></pre>
范例:
/**
* 普通樣式
*
* @param context
*/
private void simpleNotify(Context context) {
initNotificationManager(context);
//為了版本兼容 選擇V7包下的NotificationCompat進行構造
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
//Ticker是狀態欄顯示的提示
builder.setTicker("簡單Notification");
//第一行內容 通常作為通知欄標題
builder.setContentTitle("標題");
//第二行內容 通常是通知正文
builder.setContentText("通知內容");
//第三行內容 通常是內容摘要什么的 在低版本機器上不一定顯示
builder.setSubText("這里顯示的是通知第三行內容!");
//ContentInfo 在通知的右側 時間的下面 用來展示一些其他信息
//builder.setContentInfo("3");
//number設計用來顯示同種通知的數量和ContentInfo的位置一樣,如果設置了ContentInfo則number會被隱藏
builder.setNumber(2);
//可以點擊通知欄的刪除按鈕刪除
builder.setAutoCancel(true);
//系統狀態欄顯示的小圖標
builder.setSmallIcon(R.drawable.notify_5);
//下拉顯示的大圖標
builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.launcher_sohu));
Intent intent = new Intent(context, PendingActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 1, intent, 0);
//點擊跳轉的intent
builder.setContentIntent(pIntent);
//通知默認的聲音 震動 呼吸燈
builder.setDefaults(NotificationCompat.DEFAULT_ALL);
Notification notification = builder.build();
notificationManager.notify(TYPE_Normal, notification);
}</code></pre>

普通樣式.png
/**
* 多文本樣式
* @param context
*/
private void bigTextStyle(Context context) {
initNotificationManager(context);
//為了版本兼容 選擇V7包下的NotificationCompat進行構造
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setContentTitle("BigTextStyle");
builder.setContentText("BigTextStyle演示示例");
builder.setSmallIcon(R.drawable.notify_5);
builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.launcher_sohu));
android.support.v4.app.NotificationCompat.BigTextStyle style = new android.support.v4.app.NotificationCompat.BigTextStyle();
style.bigText("這里是點擊通知后要顯示的正文,可以換行可以顯示很長很長很長很長很長很長很長很長很長很長很長很長很長很長很長很長很長很長");
style.setBigContentTitle("點擊后的標題");
style.setSummaryText("末尾只一行的文字內容");
builder.setStyle(style);
builder.setAutoCancel(true);
Intent intent = new Intent(context, PendingActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 1, intent, 0);
builder.setContentIntent(pIntent);
builder.setDefaults(NotificationCompat.DEFAULT_ALL);
Notification notification = builder.build();
notificationManager.notify(TYPE_BigText, notification);
}

多文本樣式.png
/**
* 最多顯示五行 再多會有截斷
*/
public void inBoxStyle(Context context) {
initNotificationManager(context);
//為了版本兼容 選擇V7包下的NotificationCompat進行構造
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setContentTitle("InboxStyle");
builder.setContentText("InboxStyle演示示例");
builder.setSmallIcon(R.drawable.notify_5);
builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.launcher_sohu));
android.support.v4.app.NotificationCompat.InboxStyle style = new android.support.v4.app.NotificationCompat.InboxStyle();
style.setBigContentTitle("BigContentTitle")
.addLine("第一行,第一行,第一行,第一行,第一行,第一行,第一行")
.addLine("第二行")
.addLine("第三行")
.addLine("第四行")
.addLine("第五行")
.setSummaryText("SummaryText");
builder.setStyle(style);
builder.setAutoCancel(true);
Intent intent = new Intent(context, PendingActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 1, intent, 0);
builder.setContentIntent(pIntent);
builder.setDefaults(NotificationCompat.DEFAULT_ALL);
Notification notification = builder.build();
notificationManager.notify(TYPE_Inbox, notification);
}

inBoxStyle.png
/**
* 大圖樣式
* @param context
*/
public void bigPictureStyle(Context context) {
initNotificationManager(context);
//為了版本兼容 選擇V7包下的NotificationCompat進行構造
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setContentTitle("BigPictureStyle");
builder.setContentText("BigPicture演示示例");
builder.setSmallIcon(R.drawable.notify_5);
builder.setDefaults(NotificationCompat.DEFAULT_ALL);
builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.launcher_sohu));
android.support.v4.app.NotificationCompat.BigPictureStyle style = new android.support.v4.app.NotificationCompat.BigPictureStyle();
style.setBigContentTitle("BigContentTitle");
style.setSummaryText("SummaryText");
style.bigPicture(BitmapFactory.decodeResource(context.getResources(), R.drawable.small));
builder.setStyle(style);
builder.setAutoCancel(true);
Intent intent = new Intent(context, PendingActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 1, intent, 0);
builder.setContentIntent(pIntent);
Notification notification = builder.build();
notificationManager.notify(TYPE_BigPicture, notification);
}

大圖樣式.png
/**
* 橫幅通知
*
* @param context
*/
private void hangup(Context context) {
initNotificationManager(context);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
Toast.makeText(context, "此類通知在Android 5.0以上版本才會有橫幅有效!", Toast.LENGTH_SHORT).show();
}
//為了版本兼容 選擇V7包下的NotificationCompat進行構造
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setContentTitle("橫幅通知");
builder.setContentText("請在設置通知管理中開啟消息橫幅提醒權限");
builder.setDefaults(NotificationCompat.DEFAULT_ALL);
builder.setSmallIcon(R.drawable.notify_5);
builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.launcher_sohu));
Intent intent = new Intent(context, PendingActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 1, intent, 0);
builder.setContentIntent(pIntent);
builder.setFullScreenIntent(pIntent, true);
builder.setAutoCancel(true);
Notification notification = builder.build();
notificationManager.notify(TYPE_Hangup, notification);
}

橫幅通知.png
自定義通知適配
默認通知不存在樣式適配的問題,因為默認通知的布局、顏色、背景什么的都是系統的,系統總會正確的顯示默認通知。但自定義通知就不一樣了,自定義通知的布局完全由我們自己掌控,我們可以為元素設置任何背景、顏色。那么,問題來了。Android通知欄的背景各種各樣,不同的ROM有不同的背景,白色、黑色、透明等。不同的Android版本通知欄背景也不一樣,一旦我們為自定義通知上的元素設置了特定背景或顏色,就肯定會帶來兼容性問題

適配的方式大概有兩種:
一種簡單粗暴:為自定義通知設置固定的背景(上圖中的360衛士就這么干的),比如黑色。那么內容自然就是白色或近似白色。這樣,在所有的手機上都能正常顯示,不會出現在黑色背景通知欄上顯示良好,到了白色背景通知欄上就幾乎啥也看不見。
另一種方案就稍微合理一些:通過讀取系統的通知欄樣式文件,獲取到title和content的顏色,進而將這個顏色設置到自定義通知上。讀取通知欄樣式文件本身有兼容性問題,不同Android版本的樣式文件有變,種方式也不是在所有手機上生效,實際測試發現,還是有小部分機型沒法讀取或是讀取到的是錯誤的。拿到title和content的顏色后,還可以通過算法(后面細說)判斷這個顏色是近似白色還是近似黑色,進而能判斷出通知欄的背景是近似黑色還是近似白色,這樣就能根據不同的通知欄背景加載不同的自定義通知布局。進而做到良好的適配。
/**
* 讀取系統通知欄顏色工具類
* Created by liuboyu on 16/12/21.
*/
public class SystemColorUtils {
private static final String DUMMY_TITLE = "DUMMY_TITLE";
private static final double COLOR_THRESHOLD = 180.0;
private static int titleColor;
/**
* 獲取通知欄顏色
*
* @param context
* @return
*/
public static int getNotificationColor(Context context) {
// if (context instanceof AppCompatActivity) {
// return getNotificationColorCompat(context);
// } else {
return getNotificationColorInternal(context);
// }
}
/**
* 當前狀態了是否為暗色
*
* @param context
* @return
*/
public static boolean isDarkNotificationBar(Context context) {
return !isColorSimilar(Color.BLACK, getNotificationColor(context));
}
/**
* notificationRoot了,不如就遍歷它,先找到其中的所有TextView
* 取字體最大的TextView作為title(這是合理的,
* 因為默認通知中最多也就4個TextView,分別是title、
* content、info、when,title肯定是字體最大,最顯眼的)
*
* @param context
* @return
*/
public static int getNotificationColorCompat(Context context) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
Notification notification = builder.build();
int layoutId = notification.contentView.getLayoutId();
ViewGroup notificationRoot = (ViewGroup) LayoutInflater.from(context).inflate(layoutId, null);
final TextView title = (TextView) notificationRoot.findViewById(android.R.id.title);
//ROM廠商會把id改掉,導致找到的title為空。
if (null == title) {
final List<TextView> textViews = new ArrayList<>();
iteratorView(notificationRoot, new Filter() {
@Override
public void filter(View view) {
if (view instanceof TextView) {
textViews.add((TextView) view);
}
}
});
float minTextSize = Integer.MIN_VALUE;
int index = 0;
for (int i = 0; i < textViews.size(); i++) {
float currentSize = textViews.get(i).getTextSize();
if (currentSize > minTextSize) {
minTextSize = currentSize;
index = i;
}
}
return textViews.get(index).getCurrentTextColor();
} else {
return title.getCurrentTextColor();
}
}
/**
* 5.0以下的機器
*
* @param context
* @return
*/
public static int getNotificationColorInternal(Context context) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
Notification notification = builder.build();
int layoutId = notification.contentView.getLayoutId();
ViewGroup notificationRoot = (ViewGroup) LayoutInflater.from(context).inflate(layoutId, null);
final TextView title = (TextView) notificationRoot.findViewById(android.R.id.title);
if (null == title) {
iteratorView(notificationRoot, new Filter() {
@Override
public void filter(View view) {
if (view instanceof TextView) {
TextView textView = (TextView) view;
if (DUMMY_TITLE.equals(textView.getText().toString())) {
titleColor = textView.getCurrentTextColor();
}
}
}
});
return titleColor;
} else {
Log.e("ddddd3ddd",""+title.getCurrentTextColor());
return title.getCurrentTextColor();
}
}
/**
* 遍歷 notificationRoot了
*
* @param view
* @param filter
*/
private static void iteratorView(View view, Filter filter) {
if (view == null || filter == null) {
return;
}
filter.filter(view);
if (view instanceof ViewGroup) {
ViewGroup container = (ViewGroup) view;
for (int i = 0, j = container.getChildCount(); i < j; i++) {
View childAt = container.getChildAt(i);
iteratorView(childAt, filter);
}
}
}
private interface Filter {
void filter(View view);
}
/**
* 使用方差來計算這個顏色是否近似黑色
*
* @param baseColor
* @param color
* @return
*/
public static boolean isColorSimilar(int baseColor, int color) {
int simpleBaseColor = baseColor | 0xff000000;
int simpleColor = color | 0xff000000;
int baseRed = Color.red(simpleBaseColor) - Color.red(simpleColor);
int baseGreen = Color.green(simpleBaseColor) - Color.green(simpleColor);
int baseBlue = Color.blue(simpleBaseColor) - Color.blue(simpleColor);
double value = Math.sqrt(baseRed * baseRed + baseGreen * baseGreen + baseBlue * baseBlue);
if (value < COLOR_THRESHOLD) {
return true;
}
return false;
}
}
使用范例:
if (SystemColorUtils.isDarkNotificationBar(context)) {
view.setTextColor(R.id.tv_title, context.getResources().getColor(R.color.white));
view.setTextColor(R.id.tv_des, context.getResources().getColor(R.color.white));
} else {
view.setTextColor(R.id.tv_title, context.getResources().getColor(R.color.black));
view.setTextColor(R.id.tv_des, context.getResources().getColor(R.color.black));
}
需要注意的是:
如果當前工程已經繼承 com.android.support:appcompat 可正常使用
如果當前工程沒有繼承 com.android.support:appcompat ,AppBaseTheme 要繼承 @android:style/Theme.DeviceDefault.Light.DarkActionBar,本人暫時也沒有搞懂這是為什么,如果哪位大神知道,請給我留言,謝謝
<style name="AppBaseTheme" parent="@android:style/Theme.DeviceDefault.Light.DarkActionBar"></style>
來自:http://www.jianshu.com/p/c4b8e6f63c3c