Android3.0以上版本支持應用自定義通知欄,使用RemoteViews作爲自定義通知欄的展示界面類(該類不繼承View)。具體實現在網上有很多,但絕大部分都不全面,開發中遇到了不少問題,記錄下來,以備後用。
一、ROM版本問題
按鈕點擊操作僅支持3.0及以上版本,3.0以下的只能展示界面,裏面定義的按鈕無法觸發;
二、自定義通知欄實現方案
NotificationCompat.Builder mBuilder = new Builder(context);
RemoteViews mRemoteViews = new RemoteViews(context.getPackageName(), param.layoutRes);
mRemoteViews.setTextViewText(R.id.txtView_notify_bar_title,
param.title);
mRemoteViews.setTextColor(R.id.txtView_notify_bar_title,
NotificationController.newInstance(context).getDefNotificationTitleColor());
mRemoteViews.setTextViewText(R.id.txtView_notify_bar_content,
param.content);
mRemoteViews.setTextColor(R.id.txtView_notify_bar_content,
NotificationController.getInstance().getDefNotificationContentColor());
showNotifyButtons(context, param, mRemoteViews);
Intent contentIntent = new Intent(context, NotificationService.class);
contentIntent.setAction(ACTION_CLICK_NOTIFY_BAR_NOTICE);
contentIntent.putExtra(PARAM_ID, param.id);
PendingIntent contentPi = PendingIntent.getService(context, param.id,
contentIntent, PendingIntent.FLAG_CANCEL_CURRENT);
// 清除消息事件
Intent deleteIntent = new Intent(context, NotificationService.class);
deleteIntent.setAction(ACTION_CLEAR_NOTIFY_BAR_NOTICE);
deleteIntent.putExtra(PARAM_ID, param.id);
PendingIntent deletePi = PendingIntent.getService(context, param.id,
deleteIntent, PendingIntent.FLAG_CANCEL_CURRENT);
mBuilder.setSmallIcon(R.drawable.ico_app)
.setContentTitle(param.title)
.setContentText(param.content)
.setTicker(param.content)
.setContentIntent(contentPi)
.setOngoing(false)
.setDeleteIntent(deletePi)
.setWhen(System.currentTimeMillis())
.setLights(Color.GREEN, 300, 3000);
if(Environment.getOSVersionCode() >= 11
&& hasButton(param)) {
mBuilder.setContent(mRemoteViews);
}
Notification notify = mBuilder.build();
notify.flags |= param.flag;
return notify;
RemoteViews封裝了很多類似View的操作方法,如setTextViewText、setTextColor、setViewVisibility、setOnClickPendingIntent等方法,具體參數都需要int型的ViewId和View的相關參數,指明對那個View的參數進行設置。
三、 通知欄背景色問題
在應用上集成自定義通知欄後,發現不管怎麼設置佈局背景色爲透明,都無法和系統風格保持一致。最終定位發現是AndroidManifest.xml 中
<uses-sdk android:minSdkVersion="8" />
設置導致默認targetSdkVersion也是8導致的。改爲
<strong> <uses-sdk android:minSdkVersion="8"
android:targetSdkVersion="9</strong>及以上<strong>"/></strong>
四、點擊通知欄按鈕收起通知欄
自定義的通知欄按鈕被點擊可以觸發事件,但是是無法自動收起的。在點擊操作觸發時可使用以下代碼收起通知欄:
/**
* Collapse status panel
*
* @param context
* the context used to fetch status bar manager
*/
private static void collapseStatusBar(Context context) {
try {
Object statusBarManager = context.getSystemService("statusbar");
Method collapse;
if (Environment.getOSVersionCode() <= 16) {
collapse = statusBarManager.getClass().getMethod("collapse");
} else {
collapse = statusBarManager.getClass().getMethod(
"collapsePanels");
}
collapse.invoke(statusBarManager);
} catch (Exception ex) {
DebugLog.d(TAG, "collapseStatusBar() ", ex);
}
}
五、通知欄文字顏色
1、 自定義樣式方法:
Android 2.3及以上通知欄的字體顏色與2.2以下的取法不同。在styles.xml中增加兩個樣式:
<style name="NotificationText">
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="NotificationTitle">
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:textStyle">bold</item>
</style>
在res目錄新建values-v9文件夾,在該文件中新建styles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="NotificationText" parent="android:TextAppearance.StatusBar.EventContent" />
<style name="NotificationTitle" parent="android:TextAppearance.StatusBar.EventContent.Title" />
</resources>
values-v9意味着API 9以上所有版本都取這裏面的定製style作爲屬性。
接着對TextView應用樣式即可。
2、 動態讀值方法
private void getNotificationColor() {
try {
Notification ntf = new Notification();
ntf.setLatestEventInfo(mContext, TEST_NOTIFICATION_TITLE, TEST_NOTIFICATION_CONTENT, null);
LinearLayout group = new LinearLayout(mContext);
ViewGroup gp = (ViewGroup) ntf.contentView.apply(mContext, group);
final int count = gp.getChildCount();
for (int i = 0; i < count; ++i) {
if (gp.getChildAt(i) instanceof TextView) {
final TextView text = (TextView) gp.getChildAt(i);
final String szText = text.getText().toString();
if (TEST_NOTIFICATION_TITLE.equals(szText)) {
mDefTitleColor = text.getTextColors().getDefaultColor();
} else if (TEST_NOTIFICATION_CONTENT.equals(szText)) {
mDefContentColor = text.getTextColors().getDefaultColor();
}
} else if (gp.getChildAt(i) instanceof ViewGroup) {
getTextColor((ViewGroup) gp.getChildAt(i));
}
}
} catch (Exception e) {
}
}
3、 部分機型適配
部分機型上文本顏色是完全隨通知欄顏色,在xml佈局中不論設顏色textcolor,或樣式style都無法改變按鈕的顏色,使用mRemoteViews.setTextColor(viewId, color)也無法改變顏色,這時候可以用SpannableString解決。
SpannableString str = new SpannableString(showText);
ForegroundColorSpan span = new ForegroundColorSpan(Color.WHITE);
str.setSpan(span, 0, showText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
mRemoteViews.setTextViewText(key, str);
六、支持的控件
按其他參考文檔說法,TextView、Button、ImageView、ImageButton等基礎控件都是支持的,不過我測試發現ImageButton不支持,只要有該控件,則自定義通知欄無法展示,改爲ImageView即可。可能與前文所述的targetVersion有關,沒有做深入分析。
七、動作響應PendingIntent
mRemoteViews.setOnClickPendingIntent(key,
getViewOnClickIntent(context, param.id, key));
PendingIntent包裹一個Intent,在條件觸發時纔會執行Intent動作。自定義通知欄按鈕點擊事件就是一個PendingIntent,示例如下:
private static PendingIntent getViewOnClickIntent(Context context, int paramId, int btnResId) {
Intent intent = new Intent(ACTION_NOTIFY_BAR_BTN_CLICKED);
intent.putExtra(PARAM_ID, paramId);
intent.putExtra(BTN_CLICKED, btnResId);
PendingIntent pendingIntent =
PendingIntent.getService(context,
paramId + btnResId, intent, PendingIntent.FLAG_CANCEL_CURRENT);
return pendingIntent;
}
這裏Intent的構造可以隨意,PendingIntent.getService、getActivity、getBroadcast皆可使用。第二個餐食requestCode和第四個參數flags可以起到更新通知欄的作用。
參考資料:
1、 http://blog.sina.com.cn/s/blog_80a855370101hqsq.html
2、 http://www.tuicool.com/articles/JZ7Bbu
3、 http://blog.csdn.net/asce1885/article/details/7802627
4、 http://blog.csdn.net/vipzjyno1/article/details/25248021
5、 http://www.androideng.com/?p=1069