使用 Palette 讓你的 UI 色彩與內容更貼合

weixin_34127717發表於2017-09-08

版權宣告:

本賬號釋出文章均來自公眾號,承香墨影(cxmyDev),版權歸承香墨影所有。

每週會統一更新到這裡,如果喜歡,可關注公眾號獲取最新文章。

未經允許,不得轉載。

一、前言

今天介紹一個 Android 下比較有意思的 Support v7 庫,Palette,它翻譯過來就是調色盤。

Palette 可以從一張 Bitmap 中提取出它突出的顏色,這樣我們就可以將提取出來的顏色設定在 App 的固定 UI 中(例如:ToolBar 的背景),使得 UI 頁面的整體風格更加的美觀和融洽。

比如,對於一些影視類的 App,視訊詳情頁的主題都是視訊的海報,那麼對於頁面背景,我們可以提取視訊海報的顏色,設定在背景上,使得效果更佳柔和美觀。

Palette 是一個 Support v7 的包,如果使用 Gradle 引入依賴,這裡使用最新的 26.+。

compile "com.android.support:palette-v7:26.+"

二、Palette 的使用

Palette 使用起來非常的簡單,既然目的是從一個圖片中提取顏色,它的步驟就有:

  1. 傳遞一個 Bitmap,得到一個 Palette。

  2. 通過 Palette 提取需要的顏色。

就是這麼簡單,如同要將大象放冰箱,需要幾步一樣清晰。

那麼接下來我們先來了解它使用的細節。

2.1 傳遞 Bitmap 得到一個 Palette

Palette 在舊版本上有一些 generate() 的方法,用於生成一個 Palette 物件,但是在新版本上已經被標記為 @Depercated 了,所以這裡不推薦使用。

而在新版上,推薦使用 Palette.Builder 來建立我們的 Palette 物件,我們可以通過 from() 方法使用它。

/p-from.png

一般我們使用第一個方法即可,直接傳遞進去一個 Bitmap 物件。得到 Builder 之後,我們還可以配置一些規則,但是一般我們不需要進行額外的(後面會講到)。再通過 Builder.generate() 即可得到我們需要的 Palette 物件了。

2.2 通過 Palette 提取顏色

Palette 從圖片中提取的顏色,有很多選擇。這裡又涉及到另外一個類,Swatch 。

Palette 可被提取的每個顏色,都被封裝成一個 Swatch 物件,用來管理多種顏色。

這些 Swatch 有:

  • DominantSwatch

  • VibrantSwatch

  • DarkVibrantSwatch

  • LightVibrantSwatch

  • MutedSwatch

  • DarkMutedSwatch

  • LightMutedSwatch

其實這些 Swatch,真的不太好解釋其意義,唯一特別一點的就是 DominantSwatch ,它是從圖片中提取的最突出的顏色。

這些 Swatch 在 Palette 都提供了對應的 getXxx() 方法獲得。不過需要注意的是,這些 getXxx() 方法可能會得到一個 null ,因為有些顏色是沒有的。

如果只是需要得到一個顏色值,Palette 同時也提供了對應的 getXxxColor() 方法,方便我們使用。

得到 Swatch 物件之後,就可以通過對應的 Swatch 中對應的 Api 獲取我們需要的顏色值。

  • getPopulation() :Swatch 中的畫素個數。

  • getRgb():顏色的 RGB 值。

  • getHsl():顏色的 HSL 值。

  • getBodyTextColor():對應的文字顏色值。

  • getTitleTextColor():對應的標題文字顏色值。

通常來說,我們只需要通過 getRgb() 獲取到對應的顏色設定在背景上,如果背景之上還有文字內容,可以通過 getBodyTextColor() 提取出與背景匹配的文字顏色值,這樣可以顯得更加的柔和,讓文字看起來更清晰和舒服。比如,如果一個深色的背景,為它設定一個預設的深色文字,基本上就看不見了,因為對比對太弱。

2.3 舉個例子

到這裡,基本上 Palette 的基本 Api 就講解清楚了,下面舉個實際的例子來看看。

這裡找了三張 Eason 的海報,用於做 Palette 的 Demo 資源,間隔去替換圖片,然後分別提取出對應的顏色和字型顏色,設定在下面按鈕的背景上,然後每 3s 切換一張圖片。

因為有一些圖片,獲取的 Swatch 可能會返回 null ,所以這裡用了一個比價扎眼的紅色,作為錯誤色。

以下是獲取 Swatch 的程式碼。

/p-changeColor.png

接下來通過 Swatch 提取我們需要的顏色。

/p-setViewColor.png

這裡分別獲取了需要的顏色以及字型顏色,下面看看執行的效果:

/p-run.gif

可以看到,確實有一些顏色,被標記成了紅色,說明當前圖片有獲取不到的對應顏色。

三、分析 Palette 的實現

3.1 Palette 的主線邏輯

繼續深入看看 Palette 的實現原理,先從主線開始看。

Builder.generate() 開始。

/p-gen.jpg

從程式碼中可以看到,在 generate() 中,主線邏輯:

  1. 首先會通過 scaleBitmapDown() 方法,將圖片壓縮成一個小畫素的,等於生成了一個新的 Bitmap 物件,這樣有利於記憶體的管理,並且也減少了計算量。

  2. 然後再通過 mRegion 判斷是否只是提取圖片的某個區域,預設是完整的圖片全部提取,當然也可以對 mRegion 進行配置。

  3. 之後再構造一個 ColorCutQuantizer 物件,使用它的 getQuantizedColors() 方法得到 Swatch。

  4. 使用完前面壓縮的 Bitmap 物件之後,再使用 recycle() 將其回收掉。

  5. 最後,通過 Palette 本身的建構函式,去生成一個 Palette 物件,返回出去。

接下來看看比較關鍵的 ColorCutQuantizer 中的實現邏輯。

/p-quan.jpg

從程式碼中可以看到,其中的邏輯還是很清晰的。

  1. 首先通過 quantizeFromRgb888() 方法,將每個畫素的顏色進行量化,類似於將每個顏色取一個靠近的設定。舉個不恰當的例子,將不同深度的紅,都標記成紅色。

  2. 再通過 shouldIgnoreColor() 過濾掉不需要的顏色。

  3. 最終獲取到的顏色,如果小於等於我們設定的 maxColors,就可以通過 approximateToRgb888() 生成一批 Swatch。

  4. 如果大於 maxColors,就再通過 quantizePixels() 去掉一些雜色。

  5. 無論如何,最終操作的就是這裡得到的 mQuantizedColors 物件。

3.2 Swatch 的 Target

所有需要的 Swatch ,都是被 Target 物件所標記。不同的 Swatch 都是通過 Target 中標記的常量值,進行運算,得到行的顏色。

/p-target.png

3.3 過濾掉不需要的顏色

Palette 可以設定一些我們不需要的顏色,讓它們不參與運算。這裡的過濾條件,通過 Filter 來設定,並且 Palette 也提供給了一個 DEFAULT_FALTER 來標記預設的過濾顏色。

/p-filter.png

可以看到,預設的 Filter 會過濾掉一些靠近黑和白的顏色。

當然,我們也可以自己定義 Filter ,並通過 Palette 中的 addFilter()clearFilters() 來管理它。

/p-filtermethod.png

這裡儲存 Filter 的是一個 ArrayList ,所以我們是可以定義很多個 Filter 加入進去的,它們都會生效。

3.4 設定 MaxColor

在 ColorCutQuantizer 中,被使用的 maxColor ,主要用於標記需要使用的顏色個數。它是可以通過 maximumColorCount() 方法,進行設定的,如果不對其進行設定,預設值為 16。

/p-maxcolor.png

理論上來說,這裡設定的maxColor 的值越大,運算花費的時間就越長。而越小,可以被選擇的色值也就越少。

所以最佳的做法是根據當前 Bitmap 的用途來決定,色彩越豐富的圖,設定的 maxColor 越大,即可。不過正常來說也不需要額外的設定,預設的配置就挺好用了。

四、小結

到這裡就分析完 Palette 的所有相關的內容,不要僅僅滿足使用。實際上看了 Palette 的原始碼,對色彩的運算,也有了更加深入的瞭解。

公眾號二維碼.jpg

相關文章