Android 自定义Notification颜色适配问题

最近完成了一个需要自定义RemoteViews的Notification的需求,期间遇到了不少关于颜色适配的问题,在此做一下总结
在Notification上使用我们自定义的RemoteViews时,通知栏的背景色、标题文字色跟内容文字色是我们需要注意的三种颜色,如果设置不当,可能会导致我们自定义的通知栏的通知看不清楚
一般而言,我们可以通过:

不设置自定义view的背景色,标题与内容设置系统的style来解决这个问题:
SDK21以下:

1
2
3
4
// 标题样式
android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent.Title"
// 内容样式
android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent"

SDK21以及以上:

1
2
3
4
// 标题样式
android:textAppearance="@android:style/TextAppearance.Material.Notification.Title"
// 内容样式
android:textAppearance="@android:style/TextAppearance.Material.Notification.Info"

但是实际测试中发现,在某些国产机型上会出现设置错误的标题跟内容文字颜色的问题
因此,这边通过先构造一个系统的Notification,再取出其中对应颜色的方法,写了一个获取通知栏颜色的工具类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/**
* Created by superxlcr on 2018/2/4.
*
* 获取通知栏颜色工具类
*/

public class NotificationUtils {

//<editor-fold desc="property">

private static final String TITLE = "title";
private static final String CONTENT = "content";
private static final float COLOR_THRESHOLD = 180f;

private static int backgroundColor = Color.TRANSPARENT;
private static int titleColor = Color.WHITE;
private static int contentColor = Color.WHITE;
private static boolean checkDeviceColors = false;
//</editor-fold>

//<editor-fold desc="public">

/**
* 获取Notification背景色
*
* @param context 上下文
* @return 背景色,获取失败时默认为 透明色
*/
public static int getBackgroundColor(Context context) {
checkDeviceColors(context);
return backgroundColor;
}

/**
* 获取Notification标题色
*
* @param context 上下文
* @return 标题色,获取失败时默认为 白色
*/
public static int getTitleColor(Context context) {
checkDeviceColors(context);
return titleColor;
}

/**
* 获取Notification内容色
*
* @param context 上下文
* @return 内容色,获取失败时默认为 白色
*/
public static int getContentColor(Context context) {
checkDeviceColors(context);
return contentColor;
}
//</editor-fold>


//<editor-fold desc="private">

private NotificationUtils() {
}

private static void checkDeviceColors(Context context) {
if (checkDeviceColors || context == null) {
return;
}
checkDeviceColors = true;
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
Notification notification = builder.setContentTitle(TITLE).setContentText(CONTENT).build();
View notificationView = notification.contentView.apply(context, new LinearLayout(context));
if (notificationView instanceof ViewGroup) {
TextView title = findTextViewByText((ViewGroup) notificationView, TITLE);
if (title != null) {
titleColor = title.getCurrentTextColor();
// 黑色标题使用白色背景,其他标题使用透明背景
if (isSimilarColor(Color.BLACK, titleColor)) {
backgroundColor = Color.WHITE;
} else {
backgroundColor = Color.TRANSPARENT;
}
}
TextView content = findTextViewByText((ViewGroup) notificationView, CONTENT);
if (content != null) {
contentColor = content.getCurrentTextColor();
}
}
}

private static TextView findTextViewByText(ViewGroup viewGroup, String text) {
if (viewGroup == null) {
return null;
}
int size = viewGroup.getChildCount();
for (int i = 0; i < size; i++) {
View view = viewGroup.getChildAt(i);
if (view instanceof TextView) {
TextView textView = (TextView) view;
if (TextUtils.equals(textView.getText(), text)) {
return textView;
}
} else if (view instanceof ViewGroup) {
TextView textView = findTextViewByText((ViewGroup) view, text);
if (textView != null) {
return null;
}
}
}
return null;
}

private static boolean isSimilarColor(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);
return value < COLOR_THRESHOLD;
}
//</editor-fold>
}

其中,由于Android的通知栏底色一般为白色、黑色或者透明,因此在工具类中根据标题文字色的颜色,为背景对应设置白色或透明