目前android碎片化嚴重,不少廠商會針對android系統底層進行改造,通知欄也不例外,相似小米手機或者華爲的一些手機,通知欄就不是原生android的,有些通知欄的背景是黑色,有些通知欄的背景是白色,因此在應用中須要自定義通知欄的時候,很難去適配機器自身通知欄的樣式,目前不少應用在實現自定義通知欄的時候,爲了適配全部機型,一般會在自定義通知欄的時候加一層背景,例以下面的360:java
適配的方式大概有兩種,一種簡單粗暴:爲自定義通知設置固定的背景(上圖中的360衛士就這麼幹的),好比黑色。那麼內容天然就是白色或近似白色。這樣,在全部的手機上都能正常顯示,不會出如今黑色背景通知欄上顯示良好,到了白色背景通知欄上就幾乎啥也看不見。使用這種方案的應用太多了。我我的很不推崇這種方式,這樣會使得自定義通知在將近一半的手機上顯示得很突兀,和系統的通知欄不夠沉浸,影響總體美觀。另外一種方案就稍微合理一些:經過讀取系統的通知欄樣式文件,獲取到title和content的顏色,進而將這個顏色設置到自定義通知上。讀取通知欄樣式文件自己有兼容性問題,不一樣Android版本的樣式文件有變,具體可參考這篇博客 通知欄設置系統字體顏色 ,這種方式也不是在全部手機上生效,實際測試發現,仍是有小部分機型無法讀取或是讀取到的是錯誤的。拿到title和content的顏色後,還能夠經過算法(後面細說)判斷這個顏色是近似白色仍是近似黑色,進而能判斷出通知欄的背景是近似黑色仍是近似白色,這樣就能根據不一樣的通知欄背景加載不一樣的自定義通知佈局。進而作到良好的適配。android
/** * 通知欄的幫助類,提供查詢手機是否禁止通知欄,判斷通知欄背景顏色 * Created by dengqu on 2016/12/12. */ public class NotificationsUtils { private final static String TAG = NotificationsUtils.class.getSimpleName(); private static final String CHECK_OP_NO_THROW = "checkOpNoThrow"; private static final String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION"; private static final double COLOR_THRESHOLD = 180.0; private static int titleColor; /** * 判斷應用通知欄是否開啓權限 * * @param context * @return */ public static boolean isNotificationEnabled(Context context) { try { if (AndroidConfig.getAndroidVersion() >= Build.VERSION_CODES.KITKAT) { AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); ApplicationInfo appInfo = context.getApplicationInfo(); String pkg = context.getApplicationContext().getPackageName(); int uid = appInfo.uid; Class appOpsClass = null; appOpsClass = Class.forName(AppOpsManager.class.getName()); Method checkOpNoThrowMethod = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE, Integer.TYPE, String.class); Field opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION); int value = (int) opPostNotificationValue.get(Integer.class); return ((int) checkOpNoThrowMethod.invoke(mAppOps, value, uid, pkg) == AppOpsManager.MODE_ALLOWED); } } catch (Exception e) { XLLog.e(TAG, e); } return true; } /** * 判斷通知欄背景顏色,如今手機通知欄大部分不是白色就是黑色背景 * * @param context * @return */ public static boolean isDarkNotiFicationBar(Context context) { return !isColorSimilar(Color.BLACK, getNotificationColor(context)); } private static int getNotificationColor(Context context) { if (context instanceof AppCompatActivity) { return getNotificationColorCompat(context); } else { return getNotificationColorInternal(context); } } private 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; } private static int getNotificationColorInternal(Context context) { final String DUMMY_TITLE = "DUMMY_TITLE"; NotificationCompat.Builder builder = new NotificationCompat.Builder(context); builder.setContentText(DUMMY_TITLE); Notification notification = builder.build(); ViewGroup notificationRoot = (ViewGroup) notification.contentView.apply(context, new FrameLayout(context)); final TextView titleView = (TextView) notificationRoot.findViewById(android.R.id.title); if (titleView == null) { iteratoryView(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 { return titleView.getCurrentTextColor(); } } private 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 titleView = (TextView) notificationRoot.findViewById(android.R.id.title); if (titleView == null) { final List<TextView> textViews = new ArrayList<>(); iteratoryView(notificationRoot, new Filter() { @Override public void filter(View view) { textViews.add((TextView) view); } }); float minTextSize = Integer.MIN_VALUE; int index = 0; for (int i = 0, j = textViews.size(); i < j; i++) { float currentSize = textViews.get(i).getTextSize(); if (currentSize > minTextSize) { minTextSize = currentSize; index = i; } } return textViews.get(index).getCurrentTextColor(); } else { return titleView.getCurrentTextColor(); } } private static void iteratoryView(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 child = container.getChildAt(i); iteratoryView(child, filter); } } } private interface Filter { void filter(View view); } }