android 5.0以上通知欄、狀態欄圖標變成白色

原文地址:http://blog.csdn.net/gjy211/article/details/52189692


在5.0以上的系統上發現,平常的自定義notification出來的icon,居然在狀態欄上變成了純白色的icon。

查看源碼可知道:

protected void applyColorsAndBackgrounds(StatusBarNotification sbn,
            NotificationData.Entry entry) {

        if (entry.expanded.getId() != com.Android.internal.R.id.status_bar_latest_event_content) {
            // Using custom RemoteViews
            if (entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
                    && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP) {
                entry.row.setShowingLegacyBackground(true);
                entry.legacy = true;
            }
        } else {
            // Using platform templates
            final int color = sbn.getNotification().color;
            if (isMediaNotification(entry)) {
                entry.row.setTintColor(color == Notification.COLOR_DEFAULT
                        ? mContext.getResources().getColor(
                                R.color.notification_material_background_media_default_color)
                        : color);
            }
        }

        if (entry.icon != null) {
            if (entry.targetSdk >= Build.VERSION_CODES.LOLLIPOP) {
                entry.icon.setColorFilter(mContext.getResources().getColor(android.R.color.white));
            } else {
                entry.icon.setColorFilter(null);
            }
        }
    }

github:

 
因爲google在android5.0上面做了限制,爲了統一系統風格。之後的狀態欄icon就不能夠隨便用一張色彩豐富的圖片了,只能夠有白色和透明兩個顏色出現。
5.0以上(不包含5.0),系統默認通知欄圖標爲系統啓動圖標,會自動將通知欄的圖標(有色區域)全部填充爲白色,
像一個白色格子,這是Google 爲了實現材料設計規範,特意爲之。爲了去除白色圖標,鏤空背景即可:
所以這之後的想要設置顯示狀態欄icon不爲白色:這個icon只要背景需要透明,只讓內容塊純白色。
另類的解決辦法:(不推薦)
可以通過降低targetSdkVersion的方法,來達到顯示彩色icon,但是不支持降低targetSdkVersion的方法。
(就是把AndroidManifest.xml uses-sdk節點的targetSdkVersion屬性設置爲21以下(不含21))
 
但是爲了兼容5.0以下還正常使用:
我們判斷下就可以分別設置setSmallIcon
// 5.0
        if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP){
            notificationBuilder.setSmallIcon(R.drawable.ic_aphla_logo);
        } else {
            notificationBuilder.setSmallIcon(R.drawable.ic_logo);
        }

setLargeIcon 正常使用,不需要特別處理。使用舊的icon圖片即可。

因爲smallicon已經是透明,或者白色了,這時可以設置:
notificationBuilder.setColor();

然後就有背景顏色在通知欄上了。

----------------------------------------------------------------------

近期做項目,在項目快上線的時候突然發現android 5.0系統的手機出現了“狀態欄 圖標白板,通知欄圖標不全”的現象,

嚇了一跳,又要加班了啊!!!
在網上搜了一下,找到這篇博文 http://www.cnblogs.com/avenwu/p/4180193.html 謝謝,
直接搞定。
但其中有個問題,就此提出來。裏面提到的圖片要求,有的滿足要求還是不行,但是覺得上面的博文比較靠譜,後來跟設計部換了幾套圖片,終於成功了。
項目drawable--XXX中圖片名字要規範(保證各個drawable下內容相同的圖片,名字相同),不然可能會出現狀態欄中的圖標和通知欄中的圖標不一致。

(1)圖片要求,除去以上提的,最好白底
(2)如果開發中沒有規定一定要用5.0的SDK(21,22版本),可以把版本向下調到SDK 19 版本(或21版)的,這個“我看行”
(3)使用新的Api試試(我沒有試過,自己動手吧)
在此貼出上面博文的內容:

Android5.0通知變化淺析

目前在Android中通知的使用還是很常見的,爲了做版本兼容,常用兼容包NotificationCompat.Builder和 Notification.Builder。

  • NotificationCompat.Builder位於v4擴展包內(version 4 Support Library)
  • Notification.Builder在Android 3.0 開始引入(API level 11).

最近在Android5.0設備上發現一個問題:通知圖標突然變成了白色的方塊而不是代碼中設置的icon。

問題原因

細讀開發者文檔其實也可以發現一些線索,雖然筆者是直接查的源碼發現的問題原因。http://developer.android.com/design/patterns/notifications.html 一文的Use distinct icons部分介紹了幾點關於通知的建議,其中的有兩點是建議開發者不要做的行爲。

Don't
Place any additional alpha (dimming or fading) into your small icons and action icons; they can have anti-aliased edges, but because Android uses these icons as masks (that is, only the alpha channel is used), the image should generally be drawn at full opacity.

Don't
Use color to distinguish your app from others. Notification icons should only be a white-on-transparent background image.

簡單的說就是5.0後Android官方建議不要爲通知的圖標添加任何額外的透明度,漸變色,不要企圖用顏色將通知圖標與其他應用,比如系統應用,應用的通知圖標只能是在透明的背景上有白色的圖案。
至於原因,文檔並沒有細說,只是提到5.0系統將會在底層處理圖標,想知怎麼處理的可以參考Android SDK API level 21後的Notificaiton源碼,裏面寫的較詳細。
Project Structure
Project Structure

結合文檔提供的圖片示例,應該可以理解。
如果不遵循建議那麼有很大機率是會出上文提到問題的,爲什麼不是別然出問題呢?
這還依賴於代碼編譯的版本,根據嘗試,目前api 21以後編譯會出問題,20及以前的版本編譯不會出問題。所以解決問題比較簡單粗暴的方案是用20及更早的版本編譯代碼。但是要測底解決問題,還是得遵循文檔指導,及從新設計通知的圖標以符合要求。

源碼分析

下面看一下到底21的Android源碼裏面做了什麼操作會導致通知的圖標統統變白色。
Notification.Java

private RemoteViews applyStandardTemplate(int resId, boolean hasProgress) {
	//...
	if (mLargeIcon != null) {
         contentView.setImageViewBitmap(R.id.icon, mLargeIcon);
         processLargeLegacyIcon(mLargeIcon, contentView);
         contentView.setImageViewResource(R.id.right_icon, mSmallIcon);
         contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
         processSmallRightIcon(mSmallIcon, contentView);
     } else { // small icon at left
         contentView.setImageViewResource(R.id.icon, mSmallIcon);
         contentView.setViewVisibility(R.id.icon, View.VISIBLE);
         processSmallIconAsLarge(mSmallIcon, contentView);
    }
    //...
}
        /**
         * Recolor small icons when used in the R.id.right_icon slot.
         */
        private void processSmallRightIcon(int smallIconDrawableId,
                RemoteViews contentView) {
            if (!isLegacy() || mColorUtil.isGrayscaleIcon(mContext, smallIconDrawableId)) {
                contentView.setDrawableParameters(R.id.right_icon, false, -1,
                        0xFFFFFFFF,
                        PorterDuff.Mode.SRC_ATOP, -1);

                contentView.setInt(R.id.right_icon,
                        "setBackgroundResource",
                        R.drawable.notification_icon_legacy_bg);

                contentView.setDrawableParameters(
                        R.id.right_icon,
                        true,
                        -1,
                        resolveColor(),
                        PorterDuff.Mode.SRC_ATOP,
                        -1);
            }
        }

這裏我截取了兩段比較關鍵的代碼,在用NotificationCompat.Builder實例化我們的通知後,最終需要將各種圖標,參數配置,應用到通知視圖上面。可以看到如果我們只設置smallIcon而不設置largeIcon也是可以的,此時直接將small作爲大圖標設置給左側的id爲R.id.icon的ImageView。要注意的事一般情況下都不可以不設置smallIcon,否則通知無法正常顯示出來。
processSmallIconAsLarge方法裏面負責將我們設置的smallIcon二次處理,也就是這裏會改變我們最終看到的通知圖標,包括頂部狀態欄和下拉顯示的小圖標。

參考

  1. http://developer.android.com/design/patterns/notifications.html
  2. http://developer.android.com/guide/topics/ui/notifiers/notifications.html
--------------------------------------------------------------------------------------------

最近遇到一個問題,就是本來是彩色的圖標,結果在5.x的設備商status bar上面notification icon的顏色是黑白的,而在4.x上面是彩色的。

在SO上面找到這篇文章,http://stackoverflow.com/questions/28387602/notification-bar-icon-turns-white-in-android-5-lollipop。大意就是這個是5.x(api 21)的新行爲,如果AndroidManfifext.XML中的android:targetSdkVersion設置爲21,那麼在5.x的系統上,就是這種行爲。如果一定要顯示彩色,像4.x那樣,就設置android:targetSdkVersion爲老的api號,例如20。

正好研究了下targetSdkVersion生效的機制。對於這個例子而言,可以參看BaseStatusBar的實現代碼:

if (entry.targetSdk >= Build.VERSION_CODES.LOLLIPOP) {
    entry.icon.setColorFilter(mContext.getResources().getColor(android.R.color.white));
} else {
    entry.icon.setColorFilter(null);
}

他會根據entry.targetSdk判斷是否使用黑白模式。

這裏的targetSdk就是在android:targetSdkVersion。如果看這段代碼的調用關係,就會發現entry.targetSdk的值是通過:

ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
entry.targetSdk = info.targetSdkVersion;

賦值的,即ApplicaitonInfo.targetSdkVersion。

所以如果是在5.x的系統(sdk version是21/22)上運行應用,如果指定了targetsdkversion爲21/22,那就會生效黑白模式;如果指定了targetSdkVersion爲20或更低的值,則這段新的原色過濾的邏輯不生效,還是使用原有系統的行爲。於是如果我們想在新的系統上仍然讓應用保持舊系統的行文,就可以通過設置該屬性爲舊api版本來實現。

如果設置了targetSdkVersion爲21,但運行在舊的4.x系統上(例如api 19),那因爲實際的sdk runtime根本沒有處理這麼高targetSdkVersion的代碼,所以自然也不會生效。而且sdk的代碼一般採用>=某個版本號的方式來判斷,所以保證了寫21和寫19的效果在19的機器上是完全一樣的。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章