Android 主題動態切換框架 Prism

2021-02-14 LeanCloud通訊

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 基本上由三種對象類型構成:SetterFilterTrigger

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

相關焦點

  • Android主題換膚 無縫切換
    其實這個框架是可以拿來直接來用的,直接幾行代碼基本上就可以解決Android的主題換膚,但是作為一個程式設計師怎麼可以只是簡單的知道怎麼用就行了嗎?如果真是這樣就真的太low了。遇到一個好的開源項目我們至少需要把他的源碼大致看一下,走一下基本的流程,了解一下他的基本原理,這樣我們在技術上才會有所提升。
  • Android開發樣式和主題背景
    如果希望子視圖繼承樣式,則應該改為應用具有 android:theme 屬性的樣式。不過,您通常不會將樣式應用於各個視圖,而是將樣式作為主題背景應用於整個應用、Activity 或視圖集合。擴展和自定義樣式創建自己的樣式時,應始終擴展框架或支持庫中的現有樣式,以保持與平臺界面樣式的兼容性。要擴展樣式,請使用 parent 屬性指定要擴展的樣式。
  • Android藍牙框架
    代碼來源於Android P,本文相關代碼:client:frameworks/base/core/java/android/bluetooth/*system/bt/binder/android/bluetooth/**.aidlservie:framework/base/services/core/java/com/android/server
  • Android實現界面切換時的共享動畫效果
    最近看到一個項目上的界面切換時的過渡效果很炫,決定實現一下,先放上效果圖:效果就是在跳轉到另一個Activity時
  • 用ggplot2出GraphPad prism的圖
    就像之前介紹過的《畫個仿Excel的圖》一樣,出個GraphPad Prism的圖有何難,其實無非是顏色和主題而已。但如果有人把這些預設的東西給你整理好的話,那就相當於有了個格式刷,刷一下,立馬就換了個樣。其實為的不是以假亂真,而是直接出發表級別的圖。GraphPad Prism的圖我並不覺得好看,但為什麼說發表級別呢?
  • 愛奇藝開源Qigsaw,基於Android App Bundle的動態化框架
    Qigsaw是愛奇藝自主研發的動態化框架,其核心優勢如下:利用Android App Bundle開發套件,極速開發體驗。支持Android App Bundle所有功能特性,"山寨"Play Core Library公開接口實現,開發者閱讀官方文檔即可愉快開發。任何進程均可動態加載插件,支持Android四大組件動態加載。
  • Android 樣式系統 | 主題背景屬性
    這意味著您可以將如下代碼視為有代碼異味 (Code smell):<View …  android:background="@color/white"/>相反,您應該使用主題背景屬性,它允許您按主題更改顏色,例如,在深色主題中提供一個不同的值:<View …  android:background
  • 最新 21 款Android 自定義View及炫酷動畫開源框架,總有一款適合你!
    「等我幹IT發財了,就和你離婚」前言        最近對應用的UI視覺效果突然來了興致,所以找了一些合適開源控制項,這樣更加省時,再此分享給大家,希望能對大家有幫助,此博文介紹的都是UI上面的框架。2.SmartRefreshLayoutSmartRefreshLayout的是一個強大,穩定,成熟的下拉刷新框架,併集成各種的炫酷
  • BigCommerce主題與框架選擇教程
    簡單地說,框架是一組特性和功能,是為幫助開發過程而創建的。一旦開發了主題框架,用戶就可以創建子主題以便進一步完成自定義定製。 主題和框架之間最大的區別是: ●主題——可以立即安裝並使用功能。 ●框架——需要進一步的開發和自定義。 那麼,要如何使用框架呢?
  • 想讓Android手機開掛,安裝Xposed框架就行了!
    Xposed(也被稱作Xposed框架、XP框架、Xposed framework),是運行於Android作業系統的一個著名的免費開源Hook框架。其通過替換Android系統的關鍵文件,可以攔截幾乎所有Java函數的調用,並允許通過模塊擴展方式來實現各種功能,模塊中的自定義代碼可以更改調用函數時的行為,常被用來修改Android系統和應用程式的功能。
  • 技術一面:說說Android動態換膚實現原理
    換膚分為動態換膚和靜態換膚靜態換膚這種換膚的方式,也就是我們所說的內置換膚,就是在APP內部放置多套相同的資源。進行資源的切換。
  • Android 動畫
    概述動畫的本質,其實就是把內容的兩個狀態平滑的過度,而不是直接切換。
  • Android 沉浸式狀態欄攻略 讓你的狀態欄變色吧
    注意我們的主題是基於NoActionBar的,android:windowTranslucentStatus這個屬性是v19開始引入的。(二)布局文件activity_main.xml<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto
  • 教你如何開發一款實用的完整Android App,附源碼
    最近利用閒暇時間,寫了一款生活工具類的應用,開始的目的也主要是為了熟練一些老框架和熟悉一些新框架或者第三方庫,大家可以把它看成一款練手的 Demo 應用吧!應用的整體框架(Rxjava + Retrofit + okhttp 網絡請求框架)在以前的項目中採用的網絡請求框架,基本上都是使用的封裝好的 okhttp 框架,不過最近一兩年,Rxjava + Retrofit 十分熱門勢頭很盛。作為開發者,總是要去學習和接受採用這些新出來的技術。
  • Android手機QQ空間新版:玩轉GIF動態說說
    近日,Android版手機QQ空間2.4 全新發布,新版可支持GIF動態說說、多圖上傳等功能,並在 「可能認識的人」裡顯示用戶真實頭像,方便發現遺漏的好友,拓展自己的關係鏈。同時,它還全面優化了界面設計和操作設計。
  • Android使用ViewPager + Fragment實現無限滑動效果
    第二種方法的缺點是第一個和最後一個元素切換效果可能不是太好。2.1 第一種實現方法Integer.MAX_VALUE<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
  • 盤點最受開發者喜愛的Android 5.0 Lollipop API
    開發者可通過尋找視圖ID以及創建Intent和Bundle來定義不同Activity之間的動畫切換,再啟動Activity(startActivity()函數)就大功告成了。Android 5.0自帶Leanback等程序庫,Android TV輸入框架和aka TIF(幫助TV應用處理來自HDMI 輸入、電視調諧器和 IPTV 接收器的視頻流)框架,在精簡代碼的同時,讓應用UI與Android設備完美融合。Android 5.0 棒棒糖的「Overview(概覽)」是傳統多任務視窗的升級版,全新的UI呈現卡片重疊的即視感,清新又簡潔。
  • 使用Junit對Android應用進行單元測試
    本文將指導讀者如何將Android Junit框架應用到Android應用中去。本文還特別重點展示了如何測試Android中的Activity和如何識別程序中的錯誤。><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical" android:layout_width="fill_parent"    android:layout_height="fill_parent">
  • Android Spinner下拉框的基本使用
    ;java </item><item>php</item><item>xml</item><item>html</item></string-array>4、在布局文件xml的Spinner下添加:android
  • Android 屬性動畫
    屬性動畫框架屬性動畫可以使用 ViewPropertyAnimator、ObjectAnimator、ValueAnimator 這三種 Animator。它們其實是一種遞進的關係:從左到右依次變得更加難用,也更加靈活。