Android Notification 使用
wsjonly
8年前
<h3>如何创建通知</h3> <p>随着Android系统不断升级,Notification的创建方式也随之变化,主要变化如下:</p> <p>Android 3.0之前</p> <p>Android 3.0 (API level 11)之前,使用new Notification()方式创建通知:</p> <pre> <code class="language-java">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> <p>Android 3.0 (API level 11)及更高版本</p> <p>Android 3.0开始弃用new Notification()方式,改用Notification.Builder()来创建通知:</p> <pre> <code class="language-java">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> <p>这里需要注意:</p> <p>"build()" 是Androdi 4.1(API level 16)加入的,用以替代</p> <p>"getNotification()"。API level 16开始弃用"getNotification()"</p> <p>兼容Android 3.0之前的版本</p> <p>为了兼容API level 11之前的版本,v4 Support Library中提供了</p> <p>NotificationCompat.Builder()这个替代方法。它与Notification.Builder()类似,二者没有太大区别。</p> <pre> <code class="language-java">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> <p>范例:</p> <pre> <code class="language-java">/** * 普通样式 * * @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> <p><img src="https://simg.open-open.com/show/b2d208820f74195255482a0cc9431179.png"></p> <p style="text-align:center">普通样式.png</p> <pre> <code class="language-java">/** * 多文本样式 * @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); }</code></pre> <p><img src="https://simg.open-open.com/show/a1f247a72c2975abc58990bf63188b85.png"></p> <p style="text-align:center">多文本样式.png</p> <pre> <code class="language-java">/** * 最多显示五行 再多会有截断 */ 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); }</code></pre> <p><img src="https://simg.open-open.com/show/aaa685905871a85472bfe89950a2d2ba.png"></p> <p style="text-align:center">inBoxStyle.png</p> <pre> <code class="language-java">/** * 大图样式 * @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); }</code></pre> <p><img src="https://simg.open-open.com/show/3f2f8756ee3e5b0b0c7d60f3b58d3d30.png"></p> <p style="text-align:center">大图样式.png</p> <pre> <code class="language-java">/** * 横幅通知 * * @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); }</code></pre> <p><img src="https://simg.open-open.com/show/8f790185b5f2ba01ae4ed7fb07ec14a5.png"></p> <p style="text-align:center">横幅通知.png</p> <p>自定义通知适配</p> <p>默认通知不存在样式适配的问题,因为默认通知的布局、颜色、背景什么的都是系统的,系统总会正确的显示默认通知。但自定义通知就不一样了,自定义通知的布局完全由我们自己掌控,我们可以为元素设置任何背景、颜色。那么,问题来了。Android通知栏的背景各种各样,不同的ROM有不同的背景,白色、黑色、透明等。不同的Android版本通知栏背景也不一样,一旦我们为自定义通知上的元素设置了特定背景或颜色,就肯定会带来兼容性问题</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/a2e11bccb99d9c2c08e0ce3355285081.png"></p> <p>适配的方式大概有两种:</p> <p>一种简单粗暴:为自定义通知设置固定的背景(上图中的360卫士就这么干的),比如黑色。那么内容自然就是白色或近似白色。这样,在所有的手机上都能正常显示,不会出现在黑色背景通知栏上显示良好,到了白色背景通知栏上就几乎啥也看不见。</p> <p>另一种方案就稍微合理一些:通过读取系统的通知栏样式文件,获取到title和content的颜色,进而将这个颜色设置到自定义通知上。读取通知栏样式文件本身有兼容性问题,不同Android版本的样式文件有变,种方式也不是在所有手机上生效,实际测试发现,还是有小部分机型没法读取或是读取到的是错误的。拿到title和content的颜色后,还可以通过算法(后面细说)判断这个颜色是近似白色还是近似黑色,进而能判断出通知栏的背景是近似黑色还是近似白色,这样就能根据不同的通知栏背景加载不同的自定义通知布局。进而做到良好的适配。</p> <pre> <code class="language-java">/** * 读取系统通知栏颜色工具类 * 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; } }</code></pre> <p>使用范例:</p> <pre> <code class="language-java">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)); }</code></pre> <p>需要注意的是:</p> <p>如果当前工程已经继承 com.android.support:appcompat 可正常使用</p> <p>如果当前工程没有继承 com.android.support:appcompat ,AppBaseTheme 要继承 @android:style/Theme.DeviceDefault.Light.DarkActionBar,本人暂时也没有搞懂这是为什么,如果哪位大神知道,请给我留言,谢谢</p> <p><style name="AppBaseTheme" parent="@android:style/Theme.DeviceDefault.Light.DarkActionBar"></style></p> <p> </p> <p>来自:http://www.jianshu.com/p/c4b8e6f63c3c</p> <p> </p>