Prism(稜鏡) 是一款全新的 Android 動態主題切換框架,雖然是頭一次發布,但它所具備的基礎功能已經足夠強大了!本文介紹了 Prism 的各種用法,希望對你會有所幫助,你也可以對它進行擴展,來滿足開發需求。
先說一下 Prism 的誕生背景。其實我沒打算一上來就寫個框架出來,當時在給 Styling Android 博客 寫一些使用 ViewPager 來實現 UI 動態著色的系列文章,文中用到的代碼被我重構成適合講解用的組件,然後我發現這些代碼可以整理成一個簡潔的 API,於是乎便有了做 Prism 框架的想法。我把 Prism 拿給我比較認可的幾個人看,他們都覺得不錯,這樣我就一點點把它做成了庫。經過反覆使用,我覺得這個 API 在保持架構簡潔的同時已經具備了很多的功能,就決定把它發布出來了跟大家分享。
Prism 分為三個獨立庫:
將它們拆分開的原因是核心庫 prism 沒有外部依賴,身量輕巧,很容易添加到項目中去,而 prism-viewpager 和 prism-palette 要依賴於外部相關的支持庫。如果項目不需要這兩個擴展庫,就沒有其他依賴了;假如應用程式用到了 ViewPager,那該項目就包含了 ViewPager 所依賴的支持庫,這時再引入 prism-viewpager 庫,其所帶來的系統開銷大可忽略不計。
Prism 已發布到 jCenter 和 Maven Central 上,如果你的項目已使用了其中一個做為依賴倉庫,那只要在 build.gradle 的 dependencies 選項下添加 Prism 庫就好。以下是添加了 prism 和 prism-viewpager 兩個庫的代碼:
目前已發布的版本是 1.0.1,最新版本的連結是 https://bintray.com/stylingandroid/maven/prism/_latestVersion。
添加好必要的依賴就可以使用 Prism 了。
Prism 基本上由三種對象類型構成:Setter、Filter 和 Trigger。
Setter 用來設置 UI 對象的顏色,一般是 View 但也可以是其他元素,後面會講到。它的基本用法是將 setColour(int colour)(或 setColor(int color))映射到 View 封裝的某個方法上。例如,內置的 ViewBackgroundSetter 會映射到 setBackgroundCOLOR(int color) 上。有時 Setter 在不同版本的 Android 上會產生不同的效果,例如 StatusBarSetter 在 Android Lollipop (5.0) 之前的系統上不起作用,因為 Lollipop 之前的版本不支持改變 StatusBar 的顏色。不過 Prism 會隨機應變,不會引起程序崩潰,請放心使用,一切交由 Setter 搞定。
Prism 內置有如下幾個基本的 Setter:
FabSetter(FloatingActionButton fab)
為 Android Design Support Library 中的 FloatingActionButton(簡寫 FAB)設置背景色。
StatusBarSetter(Window window)
設置指定窗體的狀態欄顏色,注意它的操作對象並不是 View。
TextSetter(TextView textView)
設置 TextView 中的文本顏色。
ViewBackgroundSetter(View view)
設置 View 的背景顏色。
當然,你也可以創建新的 Setter 給自定義 View 中的不同組件設置顏色,或者給同一個 View 創建多個 Setter 來設置不同的屬性,同時對不同組件進行著色。只要把自定義的 Setter 添加到 Prism 中即可生效。
Filter 可以對顏色進行轉化處理。一般向 Prism 傳入的是一個顏色值,有時我們可能需要把該顏色的不同色度應用到不同的 UI 組件上,這時要用 Filter 將顏色進行一下轉換再輸出。內置的基本 Filter 有:
IdentifyFilter()
返回與輸入相同的顏色。
ShadeFilter(float amount)
將輸入顏色與黑色混合進行加深處理。amount 為 0 到 1 之間的浮點數,代表黑色的混合比率。當 amount 為 0 時,輸出顏色就是輸入顏色;為 1 時,則輸出純黑色。
TintFilter(float amount)
將輸入顏色與白色混合進行加亮處理。amount 為 0 到 1 之間的浮點數,代表白色的混合比率。當 amount 為 0 時,輸出顏色就是輸入顏色;為 1 時,則輸出純白色。
Trigger 是顏色變化時所觸發的事件。通常它會調用 Prism 實例上的 setColour(int colour),將顏色變化的消息傳遞給在該實例上註冊過的所有 Setter 方法。
因為 Trigger 需要額外的依賴庫,所以 Prism 核心庫沒有將它包含進去,但在 ViewPager 和 Palette 的擴展庫中都有提供。
接下來我們要將 Prism 這三個組件整合起來,其實每個 Prism 實例的作用就是如此。每個實例可以有多個 Trigger 或者一個都沒有,同樣也可以有一個或多個 Setter。每個 Setter 可以綁定一個 Filter,Filter 把 Trigger 發過來的顏色轉換後再交還給 Setter。
Prism 還提供了一些智能的工廠方法,它們會為傳入的數據自動創建 Setter 方法,比如向 Prism.Builder.background() 傳入 FloatingActionButton,Prism 會自動創建出 FabColourSetter。
每個 Prism 實例會使用 builder 模式來構建和整合組件,然後與 Trigger 綁定,對觸發事件做出響應。下面來看一下如何創建一個 Prism 實例:
上面的代碼大部分都是基本的 Android 開發操作,不需要特別的解釋。
這部分是在創建 Prism 實例——先創建一個將輸入顏色加亮 50% 的 Filter(TintFilter),然後創建 Prism.Builder 實例,並添加 AppBar 實例(這會為 AppBar 創建一個 Setter 來設置背景色)、Window(為 StatusBarColour 創建 Setter 來設置狀態欄顏色)、TextView(使用 text(TextView) 來設置文字顏色),以及 FloatingActionButton(設置 FAB 背景色並應用第一步中的 TintFilter)。最後用 build() 來完成 Prism 實例的構建。
現在所有組件都被串聯了起來,此時只要調用該實例上的 setColour(int colour) 就可以同時改變這些組件的顏色:
代碼最後明確使用了 onDestroy() 來清除 Prism 實例。其實嚴格來說這一步並不是必須要有,因為等到 Activity 被清除後,系統不會保留對 Prism 實例的引用,垃圾回收器會將 Prism 實例處理掉。不過如果後面真不會再用的話,及時做下手工清理也無妨。
Prism 的基本用法就是這樣,只要在 onCreate() 中增加六行代碼,就能同時改變各組件的顏色(下面使用了 FloatingActionButton 來觸發顏色切換)。
把 Setter 和 Filter 配合起來使用省去了大量的樣板代碼,讓事情簡單好多,實際上它們完成的工作並不複雜,但如果搭配 Trigger 使用,情況就不一樣了。
首先將 prism-viewpager 做為依賴添加到項目中來,對應的 build.gradle 內容如下:
Trigger 是 Prism 實例最前方的關卡,它來觸發主題顏色的改變。我們先來看一下 ViewPagerTrigger 如何根據用戶操作來觸發 ViewPager 改變顏色。ViewPager 的 Adaptor 要為每個頁面位置提供顏色信息,這需要通過 ColourProvider 接口來完成(或 ColorProvider,如果不介意使用這種拼寫方式所帶來的少許性能損失的話):
如果你用過 PagerTitleStrip 或 Design Library 中的 TabLayout,那對給每個頁面位置提供一個標題的做法就不陌生了。ColourProvider 接口就是這個作用,只不過它把標題的字符串換成了 RGB 顏色值。Adapter 已內置了 getCount() 方法,所以在繼承 Adapter 時不用重新定義這個方法,可以按下面的示例來實現自己的 Adaptor:
我們得到了一個實現了 ColourProvider 接口的 Adaptor,現在可以把它跟 ViewPagerTrigger 一起使用了:
在 setupViewPager() 中,我們先創建了一個 RainbowPagerAdapter 實例,並把它應用到 ViewPager 上,然後又創建了一個加亮 FAB 背景色的 TintFilter, 以及與 ViewPager 和 Adaptor 相關聯的 Trigger。
接著以同樣的方式再創建一個 Prism 實例,這次我們為 Prism 綁定了更多的組件,並添加了剛才做好的 Trigger。你可能注意到 ViewPager 實例被設置了顏色,這會改變 ViewPager 滑動到邊界時產生的發光效果的顏色(因為不同版本的系統會用不同的方式來處理髮光效果,但 Prism 內部會處理好這些差異)。
然後把 TabLayout 和 ViewPager 進行綁定(TabLayout 要求這樣做,但 Prism 並不需要這樣),最後把 ViewPager 的初始頁面設為第一頁。好了大功告成,現在主題色會隨著標籤頁的切換而改變,請看 Demo:
細心的人可能會發現其間的顏色過渡看起來並不生硬,顏色是隨著用戶的拖拽而逐漸產生變化:
還有一些更微妙的細節。如果用戶選擇了間隔很遠的標籤頁面,正常情況會過渡顯示從開始到結束標籤之間的每種顏色,從視覺上說會略顯唐突和不自然,而 ViewPagerTrigger 只選擇開始和結束標籤的兩種顏色來做平滑過渡(也就是黃色 YELLOW 和紫色 VIOLET,跳過 GREEN、BLUE 和 INDIGO):
這是 ViewPager 滑動到邊界時的動畫效果:
最後我們來說一下 prism-palette 的用法。先將它做為依賴添加到項目中來,對應的 build.gradle 內容如下:
PaletteTrigger 使用起來非常簡單,只要創建一個 PaletteTrigger 實例,再把它添加到 Prism.Builder 上:
接下來,我們可以通過調用 PaletteTrigger 的 setBitmap(Bitmap bitmap) 方法來觸發顏色變化。這會創建一個新的 Palette 實例,等到 Palette 從圖像中提取完色樣後就去觸發 Prism。
要想正確地為相關聯的 UI 組件著色,我們需要了解 Palette 的工作原理。
Palette 可以從一張圖片中提取出最多 6 種不同的色樣:
每種色樣又可以分離出 3 種色值:
原色
適用於以原色為背景色的標題文本的色值
適用於以原色為背景色的正文的色值
這樣從 Palette 中我們可以獲取最多 18 種不同的顏色。
PrismTrigger 提供了許多工廠方法,以 Filter 的形式返回不同的色樣,通過使用 modifier 讓 Filter 決定要不要使用原色、標題顏色和正文顏色。實際上這是利用 Filter 機制為每一個與 Prism 關聯起來的 UI 組件找到合適的顏色。
例如要給標題使用「鮮豔濃」的顏色,只要將有效的工廠方法鏈式連接起來組成所需的 Filter:
如果不設置 Filter 那麼 Palette 會默認使用「鮮豔」的原色色值,但建議按需要設置好 Filter。目前,如果 Palette 沒找到指定色樣,就會應用透明效果,即把被著色的 UI 組件完全隱藏起來。這種處理方法並不理想,我們會在以後版本中做出改進。
至此 PaletteTrigger 跟 Prism 完全綁定好了:
6 個 View 對象各自採用了上述 6 種色樣的一種,2 個 TextView 中標題使用了「鮮豔」,正文了使用「柔色淺」。
你可能還注意到我們把 Activity 註冊成一個 Setter,這是為了在 Palette 完成色樣提取後收到回調,因為處理較大圖像時速度可能會慢。這樣只有等色樣提取完成後 ImageView 中的圖像才會被更新,用戶體驗會稍稍好一點,圖像更新和 UI 顏色刷新同步進行。請看 Demo:
在上面的示例中我們實際並沒綁定 UI,只是演示一下怎樣提取各種色樣以及如何應用。但根據前面講過的內容,相信加入綁定也不是難事。
這些就是 Prism 的基本用法。如果 Prism 開發還會繼續,我們會帶來更多的內容。文中的所有例子可以從 Github- Prism 源碼 中的 sample 中找到。
【拼寫】大家可能注意到了 Prism 中有些方法名稱採用的是英式拼寫習慣,比如 setColour() 中的 colour。我是英國人,我知道很多人喜歡用 color 的寫法,我尊重這種個人偏好,所以 Prism 支持兩種拼法。也就是說凡是用到 setColour(int colour) 的地方都可以替換成 setColor(int color),兩者是等價的。只不過如果使用 setColor(int color),系統內部實際會去調用 setColour(int colour),所以直接使用英式拼寫可以稍稍節省一些系統開銷。【重要提示】由於代碼版權問題,Prism 的開發計劃已無限期擱置,具體說明請參考 Prism 源碼倉庫 中的 README 內容。之所以仍然要發布這篇文章,是想讓大家了解到 Prism 現有的功能,對自己的開發有所幫助。
原文連結:https://blog.stylingandroid.com/prism-fundamentals-part-1/
譯文連結:https://blog.leancloud.cn/3612
轉載本文請標明出處:LeanCloud