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" />
<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