Android通知栏版本兼容解决方案

Android通知栏是我们在APP中几乎必须使用到的,自定义通知栏给我们带来很多拓展性,但是我们在使用自定义通知栏时往往会遇到自定义通知栏和系统本身颜色、字体等不兼容的问题,本篇博客就重点讲解如何解决Android自定义通知栏在各Android系统的适配问题。

概述:

众所周知,Android通知有两种,默认通知与自定义通知。

默认通知栏通过调用系统接口就能实现,代码如下:

new NotificationCompat.Builder(this)
                /* 状态栏相关 */
                //状态栏显示的icon,尺寸建议是24x24(尺寸太大,在某些系统,如:4.x.x就会只显示图片的中间一部分),必须设置,否则报异常
                .setSmallIcon(R.mipmap.notification_status_bar_icon)
                .setTicker("新通知提醒")
                /* 通知显示相关 */
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.chopper))
                .setContentTitle("乔巴")
                .setContentText("棉花糖")
                .setWhen(System.currentTimeMillis())
//                .setShowWhen(false) //是否显示时间
                .setContentInfo("我是右下角")
//                .setNumber(1000) //与setContentInfo()设置的内容冲突
                /* 其它 */
                .setDefaults(Notification.DEFAULT_ALL)
                .setAutoCancel(true)
                .setDeleteIntent(getDeletePendingIntent())
                .setContentIntent(getClickPendingIntent())

展现的效果如下:

自定义通知就稍微麻烦一些,需要定义一个layout文件,使用RemoteViews加载它并设置一些点击事件,再设置到builder,代码如下:

RemoteViews notifactionView = new RemoteViews(mContext.getPackageName(),
        R.layout.cl_screen_notification);
mBuilder.setContent(notifactionView);
notifactionView.setOnClickPendingIntent(R.id.cl_screen_notification, pendingIntent);
Bitmap pluginIcon = drawableToBitmap(pluginDrawable);
LogUtils.d("myl", "获得icon" + pluginIcon);
notifactionView.setImageViewBitmap(R.id.cl_plugin_icon, pluginIcon);
notifactionView.setTextViewText(R.id.dl_plugin_msg, pluginContent);
Notification notification = mBuilder.build();
int id = 199;
LogUtils.d(TAG, "创建通知");
mNotificationManager.notify(id, notification);

通知栏版本兼容问题

为什么会存在版本适配问题?在默认通知栏是不存在样式适配的问题的,因为默认通知的布局、颜色、背景什么的都是系统的,系统总会正确的显示默认通知。

但自定义通知就不同了,因为我们可以自定义设置任何背景、颜色。Android通知栏的背景各种各样,不同的ROM有不同的背景,白色、黑色、透明等。不同的Android版本通知栏背景也不一样,一旦我们为自定义通知上的元素设置了特定背景或颜色,就肯定会带来兼容性问题(主要是文本啦)。

如何解决版本兼容问题?

有以下几种解决方法:

解决方法1:自定义布局的时候,把背景颜色也给自定义了

详尽的说就是:为自定义通知设置固定的背景(上图中的360卫士就这么干的),比如黑色。那么内容自然就是白色或近似白色。这样,在所有的手机上都能正常显示,不会出现在黑色背景通知栏上显示良好,到了白色背景通知栏上就几乎啥也看不见。优缺点如下:

优点:简单粗暴,无需考虑版本兼容问题

缺点:整体可能与系统通知风格迥异

效果如下:

解决方法2:

通过读取系统的通知栏样式文件,获取到title和content的颜色,进而将这个颜色设置到自定义通知上。读取通知栏样式文件本身有兼容性问题,不同Android版本的样式文件有变,拿到title和content的颜色后,还可以通过算法(后面细说)判断这个颜色是近似白色还是近似黑色,进而能判断出通知栏的背景是近似黑色还是近似白色,这样就能根据不同的通知栏背景加载不同的自定义通知布局。进而做到良好的适配。

根据不同版本和targetSdkVersion使用对应的系统TextAppearance

运行环境Android 4.4及以下版本 or 运行环境Android 5.0,但targetSdkVersion <=19:

Title字体@android:style/TextAppearance.StatusBar.EventContent.Title

Content字体@android:style/TextAppearance.StatusBar.EventContent

Android 5.0及以上版本:

Title字体@android:style/TextAppearance.Material.Notification.Title

Content字体@android:style/TextAppearance.Material.Notification.Line2

范例如下:

  • 版本5.0以下:

    通知的背景默认系统背景,

    字体颜色@style/TextAppearance.StatusBar.EventContent.

    TextAppearance.StatusBar.EventContent, 顾名思义StatusBar的事件内容文本样式. 在系统中, 会默认匹配, 如灰色等.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tool="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="64dp"
    android:gravity="center_vertical">
    ...
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_toEndOf="@+id/notification_iv_portrait"
        android:layout_toRightOf="@+id/notification_iv_portrait"
        android:gravity="center_vertical"
        android:orientation="vertical">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/notification_widget_title"
            android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
            android:textSize="@dimen/d14sp"/>

        <TextView
            android:id="@+id/notification_tv_calories"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
            android:textSize="@dimen/d20sp"
            tool:text="10步 | 20大卡"/>
    </LinearLayout>
</RelativeLayout>
  • 版本5.0及以上

    新建layout-v21, 表面5.0版本以后使用.

    @android:style/TextAppearance.Material.Notification.Title通知栏标题.

    @android:style/TextAppearance.Material.Notification.Line2通知栏内容.

    这样就可以完全匹配系统颜色.

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tool="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="64dp"
    android:gravity="center_vertical">

    <ImageView
        android:id="@+id/notification_iv_portrait"
        android:layout_width="70dp"
        android:layout_height="64dp"
        android:paddingBottom="8dp"
        android:paddingTop="8dp"
        android:scaleType="centerInside"
        tool:src="@drawable/widget_normal"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_toRightOf="@+id/notification_iv_portrait"
        android:gravity="center_vertical"
        android:orientation="vertical">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/notification_widget_title"
            android:textAppearance="@android:style/TextAppearance.Material.Notification.Title"
            android:textSize="14sp"/>

        <TextView
            android:id="@+id/notification_tv_calories"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="@android:style/TextAppearance.Material.Notification.Line2"
            android:textSize="20sp"
            tool:text="10步 | 20大卡"/>
    </LinearLayout>
</RelativeLayout>

效果如下:

优点:整体与系统通知风格基本一致

缺点:不能很好地兼容某些魔改的系统,例如魅族的系统就无法兼容,效果如下:

解决方法3:

  • 从一个系统默认样式的Notification实例中获取其中的contentView(RemoteViews)实例
  • 调用contentView的apply方法把默认样式的Notification的整个布局绑定一个FrameLayout中
  • 从这个FrameLayout把显示Title的TextView实例找出来
  • 调用TextView的getCurrentTextColor()方法把默认样式的Title的颜色值取出来,赋给对应的自定义View

    范例如下:

public static SystemNotifictionBean getNotificationTitleColor(Context context) {
    if (context == null || sSystemNotificationBean != null) {
        return sSystemNotificationBean;
    }

    NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
    builder.setContentTitle(TITLE_STRING); //设置用于标记标题控件的字符串
    builder.setContentText(TITLE_CONTENT); //设置用于标记内容控件的字符串
    Notification notification = builder.build();
    // 利用RemoteViews的apply方法获取实例化的默认样式布局的根布局
    ViewGroup rootVG = (ViewGroup) notification.contentView.apply(context, new FrameLayout(context));
    // 遍历默认样式的布局,找出包含标题字符串的控件,找出来并记录其字体颜色值
    iteratorView(rootVG, new Filter() {
        @Override
        public void filter(View view) {
            if (view instanceof TextView) {
                TextView textView = (TextView) view;
                if (TITLE_STRING.equals(textView.getText().toString())) {
                    if (sSystemNotificationBean == null) {
                        sSystemNotificationBean = new SystemNotifictionBean();
                    }
                    sSystemNotificationBean.mTitleColor = textView.getCurrentTextColor();
                }
            }
        }
    });
    // 遍历默认样式的布局,找出包含内容字符串的控件,找出来并记录其字体颜色值
    iteratorView(rootVG, new Filter() {
        @Override
        public void filter(View view) {
            if (view instanceof TextView) {
                TextView textView = (TextView) view;
                if (TITLE_CONTENT.equals(textView.getText().toString())) {
                    if (sSystemNotificationBean == null) {
                        sSystemNotificationBean = new SystemNotifictionBean();
                    }
                    sSystemNotificationBean.mContentColor = textView.getCurrentTextColor();
                }
            }
        }
    });

    return sSystemNotificationBean;
}

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++) {
            iteratorView(container.getChildAt(i), filter);
        }
    }
}

优点:能准确适配所有系统

缺点:在原生系统运行环境Android 5.0和targetSdkVersion低于19时,会有适配问题

综合上面3中解决方法,建议使用的兼容解决方法:

1、把targetSdkVersion设置为21或以上

2、采用解决方法3的方案

温馨提示

  • SmallIcon必须设置,不设置会报异常:java.lang.IllegalArgumentException: Invalid

    notification (no valid small icon)

  • SmallIcon的尺寸最好是24x24,不然在某些机器上会显示不全
  • SmallIcon在targetSdkVersion低于21的时候是显示RGB原图,但在21及以上则会变成白色,解决方法就是使图标是只有白色一种颜色,然后借助alpha图层来绘制出一个logo的样式。
  • setNumber()和setContentInfo()都是设置通知右下角的内容,但两者存在冲突,即其中一个会无效

时间: 2024-10-03 17:45:34

Android通知栏版本兼容解决方案的相关文章

Android 实现高斯模糊效果及低版本兼容

Android 实现高斯模糊效果及低版本兼容 Hello,好久不见,好久没写博客了,总结了下原因有三,第一,变懒了:第二,变懒了:第三,还是变懒了:因为任何不更新博客的理由都是借口! 一.效果演示 项目中用到了高斯模糊效果,查阅过一些资料,考虑到性能问题最终还是选择使用Android自带的RenderScript库来实现,关于使用RenderScript来实现高斯模糊网上也有很多类似的方法,大部分都总结的比较乱,此处算是做一个整理吧,供有类似需求的同学参考及学习.           (项目效果

Android 开源项目android-open-project工具库解析之(二) 高版本向低版本兼容,多媒体相关,事件总线(订阅者模式),传感器,安全,插件化,文件

六.Android 高版本向低版本兼容 ActionBarSherlock 为Android所有版本提供统一的ActionBar,解决4.0以下ActionBar的适配问题 项目地址:https://github.com/JakeWharton/ActionBarSherlock Demo地址:https://play.google.com/store/apps/details?id=com.actionbarsherlock.sample.demos APP示例:太多了..现在连google都

从Android Studio 说开去--未来程序员工具的发展方向——版本兼容,以及为什么我们总是要学SB&quot;新&quot;东西

抽时间学习Android. 刚刚下载了 http://developer.android.com/sdk/installing/studio.html#Updating Getting Started with Android Studio 下载安装,很简单. 启动前,先把翻墙的工具准备好,并且开动之. 否则是不行了. 我再牢骚几句啊(我自己要是开公司,门口第一个标语栏就是:大声报怨.一个连抱怨都不会的人,我不信他是一个正常人,更别提创造力了.当然,我招人也只会招参与型的人,只抱怨,但事事弃权,

android SQLite 批量插入数据慢的解决方案 (正对于不同的android api 版本)

SQLite,是一款轻型的数据库,被广泛的运用到很多嵌入式的产品中,因为占用的资源非常少,二其中的操作方式几乎和我们接触的数据库不多,甚至只有几百K的他自然会被需求者青睐,下面讲一下在这样的轻型数据库中怎么对他进行一些读写操作. 之前做选择联系人的时候出现如果一个手机里联系人超过2000的话,往数据库里面插入会非常耗时,不同的手机存储的条数不同,这个存储的数量和手机的内存有很大的关系,往往取决于手机内存,下面对于数据量大的情况来写一下sqlite的批量查询. SqLite 掺入数据有几种 第一种

android studio 版本修改无效解决方案

我们都知道android的版本声明,是在AndroidManifest.xml文件里面的.例如 <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.xcode.lockcapture" android:versionCode="2" android:versionName="1.1"> 在android

Android通知栏介绍与适配总结

由于历史原因,Android在发布之初对通知栏Notification的设计相当简单,而如今面对各式各样的通知栏玩法,谷歌也不得不对其进行更新迭代调整,增加新功能的同时,也在不断地改变样式,试图迎合更多人的口味.本文总结了Android通知栏的版本迭代过程,在通知栏开发过程中所遇到的各种各样的坑,以及一些解决技巧,特别的,对于大众期盼的Android 7.0的到来,通知栏又会发生怎样的改变呢?接下来一一进行介绍. Android通知栏发展历史 首先来看一张各个Android版本通知栏消息的全家福

如何解决android 通知栏不显示的问题

android 8.0 以后的版本,在创建通知栏的时候,加了一个channelId的东西.要想在上述版本中显示通知,总共分两步 1.创建Channel if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { String channelId = "whatever"; //根据业务执行 String channelName = "whatever conent"; //这个是channelid 的解释,在安装

Android闹钟设置的解决方案

Android设置闹钟并不像IOS那样这么简单,做过Android设置闹钟的开发者都知道里面的坑有多深.下面记录一下,我解决Android闹钟设置的解决方案. 主要问题 API19开始AlarmManager的机制修改. 应用程序被Kill掉后,设置的闹钟不响. 6.0以上进入Doze模式会使JobScheduler停止工作. 手机设置重启后,闹钟失效问题. API19以上AlarmManager机制的修改 API19之前AlarmManager提供了三个设置闹钟的方法,由于业务需求闹钟只需要一

Android 之UI自适应解决方案

1.概况 作为Android开发人员,最头疼的莫过于让自己开发的程序在不同终端上面的显示效果看起来尽量一致(当然,如果要充分利用大屏幕的优势另当别论).在全球范围内来讲,android有着数以亿计的设备,其中就不乏设备分辨率多种多样,以及设备屏幕物理尺寸的多样化. 总得来说我们需要做的有三点,其一让APP的每个UI中的每个View宽和高更加灵活以适应不同分辨率.其二对于大屏幕设备(PAD)需要有不同的设计,竟可能多的展示内容,获取你整个APP的所有UI都可以做到一个布局中来.其三图标资源需提供不