實(shí)用型ColorPicker的設(shè)計(jì)與實(shí)現(xiàn)

一翁锡、前言

ColorPicker桩了,顏色選取器附帽,簡稱拾色器。
說到拾色器井誉,大家可能就會(huì)想到Photoshop, 使用得最多的應(yīng)該是設(shè)計(jì)蕉扮, 對(duì)于開發(fā)而言,平常要用到拾色器的機(jī)會(huì)不多颗圣。
如果有一天喳钟,項(xiàng)目中需要一個(gè)拾色器(多用于自定義顏色),該如何入手在岂?
今天且來給大家分享一下拾色器的設(shè)計(jì)和實(shí)現(xiàn)奔则。

二、顏色空間

要實(shí)現(xiàn)實(shí)用的拾色器蔽午,了解下顏色空間是必要的易茬。
顏色空間也稱彩色模型(又稱彩色空間或彩色系統(tǒng)),它的用途是在某些標(biāo)準(zhǔn)下用通常可接受的方式對(duì)彩色加以說明擦盾。
關(guān)于顏色空間,文末附有一些鏈接又碌,都是前人的精華總結(jié)岸蜗。
尤其是《色彩空間中的 HSL尉咕、HSV、HSB 有什么區(qū)別》中的討論(知乎大神的作答)璃岳,讀完立即醍醐灌頂年缎,茅塞頓開。

本節(jié)主要摘錄各大神的一些表述铃慷,并加以整理单芜。
為了閱讀體驗(yàn),就不逐一附上作者了犁柜,具體見文后鏈接洲鸠。

2.1 RGB空間

RGB是從顏色發(fā)光的原理來設(shè)計(jì)定的,通俗點(diǎn)說它的顏色混合方式就好像有紅馋缅、綠扒腕、藍(lán)三盞燈,當(dāng)它們的光相互疊合的時(shí)候萤悴,色彩相混瘾腰,而亮度卻等于兩者亮度之總和,越混合亮度越高覆履,即加法混合蹋盆。
紅、綠硝全、藍(lán)三個(gè)顏色通道每種色各分為256階亮度栖雾,在0時(shí)“燈”最弱——是關(guān)掉的,而在255時(shí)“燈”最亮伟众。
當(dāng)三色灰度數(shù)值相同時(shí)析藕,產(chǎn)生不同灰度值的灰色調(diào),即三色灰度都為0時(shí)赂鲤,是最暗的黑色調(diào)噪径;三色灰度都為255時(shí),是最亮的白色調(diào)数初。

2.2 HSB空間

RGB 是對(duì)機(jī)器很友好的色彩模式找爱,但并不夠人性化,因?yàn)槲覀儗?duì)色彩的認(rèn)識(shí)往往是”什么顏色泡孩?鮮艷不鮮艷车摄?亮還是暗?”。
例如吮播,我們平時(shí)描述顏色变屁,“深紫”,“淺綠”意狠,“明黃”粟关,"暗紅”等,一是交代基礎(chǔ)顏色环戈,二是對(duì)顏色本身加以描述闷板。
HSB(HSV) 基于 RGB ,是一個(gè)更人性化的表示方法院塞。
H(Hue) 為色相, 取值范圍:0-360°遮晚,基礎(chǔ)顏色。
S(Saturation) 為飽和度拦止, 取值范圍:0 - 1(0% - 100%), 表示色彩的純度县遣。
B(Brightness)為明度, 取值范圍:0 - 1(0% - 100%),表示對(duì)光量的感知汹族。
明度在某些地方也叫Value萧求,所以就有了HSV,HSV和HSB是一樣的鞠抑,只是關(guān)于明度的叫法不一樣而已饭聚。

  • 色相環(huán)的每一種顏色忌警,在RGB空間中搁拙,最多只有兩個(gè)顏色通道(r, g, b)大于0,所以色相環(huán)的顏色是最純凈的法绵。
  • 明度決定了RBG三個(gè)分量的大小箕速,也就是決定了光亮的大小,在感知層面朋譬,就是明暗的區(qū)別盐茎。
  • 飽和度為0的顏色,r徙赢,g字柠,b相等, 當(dāng)明度為0時(shí)為黑色,明度為1時(shí)為白色狡赐,大于0小于1時(shí)為灰色窑业;
    飽和度為1時(shí),顏色值僅取決于色相和明度枕屉,而明度只控制RGB分量的大小常柄,所以顏色還是純凈的;
    飽和度在0到1之間時(shí),為飽和度為0的顏色(黑灰白)和飽和度為1的顏色的線性插值西潘,越靠近1顏色越純凈卷玉。

2.3 明度

先看 Photoshop 的 HSB 顏色模型拾色器,如下圖所示喷市,HSB 的 B(明度)控制純色中混入黑色的量相种,越往上,值越大品姓,黑色越少蚂子,顏色明度越高缭黔。

2.4 飽和度

如下圖所示食茎,HSB 的 S(飽和度)控制純色中混入白色的量,越往右馏谨,值越大别渔,白色越少,顏色純度越高惧互。


2.5 色相

色相指的是色彩的外相哎媚,是在不同波長的光照射下,人眼所感覺不同的顏色喊儡,如紅色拨与、黃色、藍(lán)色等艾猜。
在HSL和HSV色彩空間中买喧,H指的就是色相,是以紅色為0°(360°)匆赃;黃色為60°淤毛;綠色為120°;青色為180°算柳;藍(lán)色為240°低淡;品紅色為300°。

從上圖可以看出瞬项,從0°到360°蔗蹋,是一個(gè)分段函數(shù),其中囱淋,每一段都有一個(gè)顏色分量是0猪杭,一個(gè)分量是1,另一個(gè)分量或從0到1绎橘,或從1到0胁孙。

2.6 HSB轉(zhuǎn)RGB

對(duì)用戶而言唠倦,HSB空間更容易調(diào)節(jié),但是對(duì)于計(jì)算機(jī)涮较,用RGB空間渲染會(huì)更加方便稠鼻。
以下是HSB到RGB的轉(zhuǎn)換公式:

圖中, h, s , v分別是色相,飽和度狂票,明度候齿。
有了這個(gè)組公式,我們就可以理解2.5節(jié)的色相條是怎么來的了闺属。
首先看到第一個(gè)公式慌盯,[]是取整符號(hào),即 hi = (int) (h/60)% 6 掂器。
令s=1, v=1, 則p=0, q=1-f, t=f亚皂。
當(dāng)i=0(第一段),(r, g, b) = (v, t, p) = (1, f, 0), 而f=h/60-hi =h/60国瓮,
也就是灭必,第一段中,r=1, g=h/60, b=0乃摹;
特例:當(dāng)h=60禁漓, r=1,g=1,b=0, 混合出黃色。
以此類推孵睬。
另外我們還注意到播歼,當(dāng)h選定之后,顏色和s掰读,v成線性關(guān)系秘狞,這一點(diǎn)對(duì)后面拾色器的實(shí)現(xiàn)很重要。

三磷支、拾色器的設(shè)計(jì)

3.1 條形拾色器

需要用到自定義顏色的APP不多谒撼,網(wǎng)易APP是其中一個(gè)食寡。
點(diǎn)擊“個(gè)性換膚->自選顏色”雾狈,會(huì)彈出這樣的界面:

頁面中間是預(yù)覽,底部是預(yù)定義的調(diào)色板抵皱;
調(diào)色板的最后善榛,是一張多彩的圖片,點(diǎn)擊后切換成兩個(gè)顏色條呻畸,如下圖:

第一個(gè)顏色條很眼熟了移盆,就是前面提到的色相;
底下這條伤为,應(yīng)該是以選取的色相為基礎(chǔ)咒循,飽和度為1据途,明度從0到1漸變。
這兩個(gè)顏色條組合起來叙甸,顏色的取值范圍為2.2節(jié)中的圓柱的側(cè)面颖医。
取值范圍雖然只占顏色空間的一部分,但是也是很有價(jià)值的一部分裆蒸。
可能網(wǎng)易的設(shè)計(jì)師只想讓用戶選取鮮明的顏色熔萧,所以舍棄了飽和度的調(diào)節(jié),同時(shí)換來了極大的簡潔性僚祷。

3.2 環(huán)形拾色器

開源的Android拾色器有不少佛致,其中HoloColorPicker是star比較多的一個(gè)項(xiàng)目。

該項(xiàng)目把色相做成一個(gè)環(huán)辙谜,底下輔以飽和度和明度的調(diào)節(jié)俺榆,可以說是一個(gè)完整的拾色器了(可以選取整個(gè)顏色空間的顏色)。
把色相做成色相環(huán)装哆,看起來比色相條要更加炫酷一些肋演,但是占用面積變大了。
像網(wǎng)易云音樂的顏色選取烂琴,因?yàn)橐o預(yù)覽圖足夠的空間爹殊,所以只能用空間占用少的拾色器;
像這種一下占用大半個(gè)屏幕的設(shè)計(jì)奸绷,不適合網(wǎng)易云音樂這種選色場(chǎng)景梗夸。

3.3 PS拾色器

Photoshop的常規(guī)拾色器是矩形構(gòu)成的“飽和度-明度”選取面板,以及色相條(然后也可以通過設(shè)置換成色相輪)号醉。

Photoshop作為專業(yè)的圖像編輯軟件反症,拾色器無疑是很強(qiáng)大的。
同時(shí)電腦顯示器的面積畢竟比手機(jī)要大很多畔派,鼠標(biāo)的選取精確度也比手指觸摸屏幕要精確铅碍,
所以Photoshop的拾色器可以大開大合,提供各種面板线椰,顯示全面的參數(shù)胞谈。

四、技術(shù)實(shí)現(xiàn)

通過前面三個(gè)小節(jié)的分析憨愉,我們可以看出烦绳,拾色器的實(shí)現(xiàn)要點(diǎn)為:
以HSB顏色空間為基礎(chǔ),通過條形配紫,環(huán)形径密,矩形等坐標(biāo)來調(diào)節(jié)HSB各分量的值,達(dá)成顏色的選取躺孝。

首先第一個(gè)要解決的問題就是享扔,顏色空間的轉(zhuǎn)換計(jì)算底桂。
幸運(yùn)的是,SDK 的 Color 類提供HSB(HSV)和RGB之間的轉(zhuǎn)換方法:

public static void colorToHSV(@ColorInt int color, @Size(3) float hsv[]) 

public static int HSVToColor(@Size(3) float hsv[])

然后要解決的第二個(gè)問題是惧眠,坐標(biāo)的繪制戚啥。
同樣,SDK也提供了各種Shader, 使得我們可以輕松的繪制各種坐標(biāo)锉试。
接下來我們結(jié)合實(shí)例看一下猫十。

4.1 仿網(wǎng)易云音樂

也不賣關(guān)子了,先上效果圖吧:

和網(wǎng)易云音樂一樣呆盖,都是在下方顯示面板拖云,有預(yù)置的調(diào)色板,調(diào)色板最后的方塊可以跳轉(zhuǎn)去自定義暗色应又;
不同之處在于宙项,比之多了透明度和純度的調(diào)節(jié),如此株扛,可以選擇整個(gè)顏色空間尤筐,以及alpha通道。

以上實(shí)現(xiàn)的關(guān)鍵點(diǎn)在于洞就,色相盆繁、飽和度(為了字體長度相同,以“純度”作為title)旬蟋,以及明度的繪制油昂。
前面我們提到,色相是分段線性變化的倾贰,因此冕碟,我們可以利用 LinearGradient 來繪制。

public LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[],
            @Nullable float positions[], @NonNull TileMode tile) 

給LinearGradient的color數(shù)組福祉匆浙,只需列出色相在分段交界的顏色即可:

private static final int[] COLORS = new int[] {
        0xFFFF0000,
        0xFFFFFF00,
        0xFF00FF00,
        0xFF00FFFF,
        0xFF0000FF,
        0xFFFF00FF,
        0xFFFF0000,
};

前面2.6節(jié)提到安寺,h確定之后, 顏色和s,v成線性關(guān)系首尼,因此挑庶,也可以通過LinearGradient來繪制飽和度和明度。
例如饰恕,繪制飽和度時(shí)挠羔,只需計(jì)算hsv第二分量(hsv[1], 也就是s)等于0和等于1時(shí)的顏色值,作為LinearGradient的colors的參數(shù)埋嵌,即可繪制在當(dāng)前h, v值對(duì)應(yīng)的s的變化(也就是飽和度對(duì)應(yīng)的顏色條)。

    hsv[1] = 0f;
    colors[0] = Color.HSVToColor(hsv);
    hsv[1] = 1f;
    colors[1] = Color.HSVToColor(hsv);

明度的繪制以此類推俱恶。

4.2 仿Photoshop

色相在0°和360°對(duì)應(yīng)的都是紅色雹嗦,首位相接范舀,所以很多時(shí)候會(huì)被做成色相環(huán)(Photoshop中叫色相輪)。
飽和度和明度了罪,如果合成一個(gè)二維坐標(biāo)锭环,會(huì)更加直觀,這樣也是Photoshop的方案泊藕。

要繪制色相環(huán)辅辩,需要用到另一個(gè)Shader:

 public SweepGradient(float cx, float cy,
            @NonNull @ColorInt int colors[], @Nullable float positions[])

用法很簡單,把上一節(jié)給出的 COLORS 代入 SweepGradient 的 colors 即可娃圆。

而要合成飽和度和明度玫锋,可以用ComposeShader:

private Shader getSVShader() {
    if (mValShader == null) {
        mValShader = new LinearGradient(
                mSVRect.left, mSVRect.top,
                mSVRect.left, mSVRect.bottom,
                Color.WHITE, Color.BLACK, Shader.TileMode.CLAMP);
    }

    if (mShaderHSV[0] != mHSV[0] || mComposeShader == null) {
        mShaderHSV[0] = mHSV[0];
        Shader satShader = new LinearGradient(
                mSVRect.left, mSVRect.top,
                mSVRect.right, mSVRect.top,
                Color.WHITE, Color.HSVToColor(mShaderHSV), Shader.TileMode.CLAMP);
        mComposeShader = new ComposeShader(mValShader, satShader, PorterDuff.Mode.MULTIPLY);
    }

    return mComposeShader;
}

ComposeShader可以組合兩個(gè)Shader, 因?yàn)轭伾蛃, v是線性關(guān)系,所以需要組合兩個(gè)LinearGradient讼呢。
第一個(gè)LinearGradient從左上角到左下角撩鹿,從白到黑;
第二個(gè)LinearGradient從左上角到右上角悦屏,從白到色相的顏色节沦。
效果如下:

既然拾色部分已經(jīng)占了這么多空間了,所以干脆把剩下的空間也用上础爬,來做數(shù)據(jù)面板甫贯;
還加一個(gè)編輯框,可以手動(dòng)輸入RGB顏色看蚜,同時(shí)限制編輯框只能輸入十六進(jìn)制获搏,限制輸入長度。

五失乾、總結(jié)

兩類拾色器中常熙,通過條形坐標(biāo)來選取顏色比較節(jié)約空間,通過環(huán)形和矩形則相對(duì)直觀碱茁。
具體使用哪一種裸卫,要視情況而定:
如果要實(shí)時(shí)預(yù)覽效果,那拾色器就不能占太多空間纽竣,這時(shí)候第一種方案會(huì)比較適合墓贿;
如果要制作調(diào)色板之類的,用第二種方案就比較高效蜓氨。

限于篇幅聋袋,在實(shí)現(xiàn)方面沒有講的很細(xì),讀者可以具體看項(xiàng)目代碼穴吹。
代碼鏈接:
https://github.com/No89757/ColorPicker

參考資料:
色彩空間中的 HSL幽勒、HSV、HSB 有什么區(qū)別
從 RGB 到 HSV 的轉(zhuǎn)換詳細(xì)介紹
RGB與HSB之間的轉(zhuǎn)換公式
維基百科 - 色相

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末港令,一起剝皮案震驚了整個(gè)濱河市啥容,隨后出現(xiàn)的幾起案子锈颗,更是在濱河造成了極大的恐慌,老刑警劉巖咪惠,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件击吱,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡遥昧,警方通過查閱死者的電腦和手機(jī)覆醇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來炭臭,“玉大人永脓,你說我怎么就攤上這事』崭浚” “怎么了憨奸?”我有些...
    開封第一講書人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長凿试。 經(jīng)常有香客問我排宰,道長,這世上最難降的妖魔是什么那婉? 我笑而不...
    開封第一講書人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任板甘,我火速辦了婚禮,結(jié)果婚禮上详炬,老公的妹妹穿的比我還像新娘盐类。我一直安慰自己,他們只是感情好呛谜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開白布在跳。 她就那樣靜靜地躺著,像睡著了一般隐岛。 火紅的嫁衣襯著肌膚如雪猫妙。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評(píng)論 1 305
  • 那天聚凹,我揣著相機(jī)與錄音割坠,去河邊找鬼。 笑死妒牙,一個(gè)胖子當(dāng)著我的面吹牛彼哼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播湘今,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼敢朱,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蔫饰,我...
    開封第一講書人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤琅豆,失蹤者是張志新(化名)和其女友劉穎愉豺,沒想到半個(gè)月后篓吁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蚪拦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年杖剪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片驰贷。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡盛嘿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出括袒,到底是詐尸還是另有隱情次兆,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布锹锰,位于F島的核電站芥炭,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏恃慧。R本人自食惡果不足惜园蝠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望痢士。 院中可真熱鬧彪薛,春花似錦、人聲如沸怠蹂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽城侧。三九已至易遣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赞庶,已是汗流浹背训挡。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留歧强,地道東北人澜薄。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像摊册,于是被迫代替她去往敵國和親肤京。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容