用ColorFilter為安卓按鈕增加效果

某天肥卡,設計師同學Syson問我,怎么在安卓上做出類似iOS上的按鈕點擊效果事镣,也就是在手指按下去的時候步鉴,按鈕中非透明的部分變暗揪胃,是不是應該給每個按鈕出兩個素材。
這個效果在iOS上是自帶的氛琢,但是該怎么在安卓實現(xiàn)呢喊递。上網(wǎng)查了查,發(fā)現(xiàn)可以使用ColorFilter對Drawable的東西做改變阳似,這樣骚勘,設計師就不用為每個按鈕出兩套素材了。

看到ColorFilter這個詞撮奏,猜猜意思大概就是類似濾鏡的存在俏讹,應該就是我想要找的東西了吧。但是剛看了一眼Drawable中的setColorFilter方法畜吊,就已經(jīng)被其中的各種PorterDuff.Mode中的模式弄暈了泽疆。官方文檔中對ColorFilter中需要設置的PorterDuff.Mode的解釋也都是我看不懂的公式-.-還好找到了幾篇比較通俗的講解,所以大概了解了一下每一種Mode直觀上的意思玲献,當然啦沒學過圖形學還是不能完完全全理解殉疼,所以這里寫的只是我覺得比較好理解的方式吧。

先要說:PorterDuff是什么捌年,因為我完全看不懂這兩個詞拼在一起是什么意思瓢娜。后來發(fā)現(xiàn),這居然是兩個人的姓礼预!Thomas Porter和Tom Duff在1984年發(fā)了一篇paper眠砾,提出了帶alpha通道的數(shù)字圖像的組合運算(可以這么說么?)逆瑞,反正就是很厲害的樣子-.-荠藤。

進入正題~
當我們描述一個像素點的時候伙单,可以用RGBA四個維度來描述它获高。其中的A是alpha,它的值在0到1之間吻育。0表示了這個像素點是完全透明的念秧。1表示了這個像素點是完全不透明的。為了方便理解布疼,假設我手頭有兩張圖片摊趾,每一張的每一個像素點,要么是全不透明游两,要么是全透明砾层,也就是說現(xiàn)在暫時沒有alpha在(0, 1)之間的像素點。
比如這樣兩張圖片:紅色的圓形和藍色的方形贱案。

紅色的圓形
藍色的方形

現(xiàn)在我們把紅色的圓形這張圖放在下面肛炮,將藍色的方形這張圖疊上去。這時,每一個像素點的位置可能會有四種情況:

  1. 這兩張圖片侨糟,在這一個位置上的像素點都是透明碍扔;
  2. 這兩張圖片,在這一個位置上的像素點都是不透明的秕重;
  3. 紅色的圓形圖片中不同,在這個位置的像素點是透明的,而藍色的方形圖片中溶耘,在這個位置上的像素點是不透明的二拐;
  4. 藍色的方形圖片中,在這個位置的像素點是透明的凳兵,而紅色的圓形圖片中卓鹿,在這個位置上的像素點是不透明的;

那么這兩張圖片上下疊起來之后留荔,最后呈現(xiàn)在這個像素點的位置的顏色應該是什么呢吟孙?如果我們找了一張新的畫布,要把最后呈現(xiàn)的樣子畫到新的畫布上聚蝶,那么新的畫布上的每個像素點杰妓,已經(jīng)默默的根據(jù)上面的四種情況被分為了四類。

現(xiàn)在我們要對新的畫布涂顏色了碘勉。新畫布上每一個像素點應該涂什么顏色呢巷挥?因為這個顏色不能憑空變出來,所以得遵循這樣的規(guī)則(為了好理解验靡,這是以alpha只能為0或1為前提的倍宾,如果alpha是其它值,就不一定是這樣啦):

像素點的類型 都是透明的 都是不透明的 只在紅色的圓形圖片中是透明的 只在藍色的方形圖片中是透明的
可以涂什么顏色 只能涂透明色 能涂透明色胜嗓、藍色或紅色 能涂透明色或藍色 能涂透明色或紅色

這樣以來高职,對于整張圖,我們一共得到了1X3X2X2也就是12種規(guī)則辞州。

這12種規(guī)則可以類比PorterDuff的12種模式怔锌,當然也是在alpha值只能為0或1的前提下。表格里“R”表示涂紅色变过,“B”表示涂藍色埃元,“0”表示涂透明色。

PorterDuff模式 都是透明的 都是不透明的 只在紅色的圓形圖片中是透明的 只在藍色的方形圖片中是透明的
CLEAR 0 0 0 0
SRC 0 B B 0
DST 0 R 0 R
SRC_OVER 0 B B R
DST_OVER 0 R B R
SRC_IN 0 B 0 0
DST_IN 0 R 0 0
SRC_OUT 0 0 B 0
DST_OUT 0 0 0 R
SRC_ATOP 0 B 0 R
DST_ATOP 0 R B 0
XOR 0 0 B R

畫圖直觀感受一下:

對紅色的圓形和藍色的方形兩張圖片應用PorterDuff的12種模式

感受就是媚狰,這好像和“邏輯運算”有著隱隱的關系-.-岛杀,這里DST代表了壓在下面的圖片,比如例子中的紅色的圓形圖片崭孤,SRC代表了疊在上面的圖片类嗤,比如例子中的藍色的方形圖片衫生。

好啦,那么在alpha可以是[0, 1]中的任意值的情況下呢土浸?
現(xiàn)在我們讓紅色的圓形和藍色的方形的都變成半透明的:

紅色的圓形罪针,色值BBFF0000
藍色的方形,色值BB0000FF

先用這張圖直觀的感受一下:


對紅色的圓形和藍色的方形兩張圖片應用PorterDuff的12種模式

能發(fā)現(xiàn)黄伊,這里產(chǎn)生了除了帶透明度的藍色泪酱、帶透明度的紅色之外的顏色,比如SRC_OVER和DST_OVER中重疊部分的顏色还最。那么這些顏色都是怎么被創(chuàng)造出來的呢墓阀?安卓的官方文檔中記載了PorterDuff的各種模式的計算公式。

官方文檔上的公式

雖然第一眼看不太懂拓轻,但是查了一下各個變量代表的意思:

Sa全稱為Source alpha表示源圖的Alpha通道斯撮;
Sc全稱為Source color表示源圖的顏色;
Da全稱為Destination alpha表示目標圖的Alpha通道扶叉;
Dc全稱為Destination color表示目標圖的顏色勿锅;
得到的結果以[alpha, color]的形式表示。

雖然我想盡力多知道一些為什么枣氧,但是好多地方還是不那么理解溢十,所以目前只能知道這些TAT:

  1. 兩張圖“重疊部分”之外的顏色,要么是透明达吞,要么就是原來的顏色张弛;
  2. CLEAR模式下,整張圖都是透明的酪劫;
  3. SRC模式下得到的圖吞鸭,就是SRC;DST模式下得到的圖覆糟,就是DST刻剥;

對了,除了這12中模式搪桂,PorterDuff中還剩下的6種模式(ADD透敌、DARKEN盯滚、LIGHTEN踢械、MULTIPLY、OVERLAY魄藕、SCREEN)是什么呢内列?
如果我沒理解錯的話,這6種模式得到的效果背率,和Photoshop里對應的混合模式得到的效果是一致的(好像MULTIPLY是不完全一致0.0)话瞧。

效果:

另外6種PorterDuff模式

終于把PorterDuff的幾種模式整理了一下嫩与,現(xiàn)在回到最開頭的問題:應該怎么在安卓的button中實現(xiàn)iOS上的點擊效果呢?
Drawable類中有一個setColorFilter()方法交排,它可以接收兩個參數(shù):顏色的色值和PorterDuff模式划滋。通過setColorFilter()設置顏色,相當于在Drawable對象上加上了一層純色埃篓,并采用對應PorterDuff模式來顯示得到最終效果处坪。
通過Button類中getBackground(),我們可以得到按鈕的圖標對應的Drawable對象架专,setColorFilter()方法正好可以施加在按鈕的圖標上同窘。
考慮到我們需要得到的最終效果是,背景圖案上透明的部分仍然透明部脚,而背景圖案上不透明的部分蒙上設置的顏色想邦,所以可以選擇SRC_ATOP模式或者MULTIPLY模式。

使用SRC_ATOP模式的代碼片段:

button.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View view, MotionEvent event) {
        Button touchedButton = (Button)view;
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touchedButton.getBackground().setColorFilter(0x77FFFF00, PorterDuff.Mode.SRC_ATOP);
                touchedButton.invalidate();
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                touchedButton.getBackground().clearColorFilter();
                touchedButton.invalidate();
                break;
        }
        return true;
    }
});

效果:

按鈕正常狀態(tài)時
按鈕被按下時

寫這篇文章時遇到的一個問題:

文章中紅色圓形和藍色方塊的例子委刘,是我在安卓sample code的基礎上加以修改然后生成的圖片丧没。(雖然傳說android studio自帶sample code,但是不知道我這出了什么問題沒弄出來锡移,所以參考的是這里的骂铁。)這段代碼生成出來的圖片應該是這樣的(隨便google一下全是這張圖-.-):

嗯,就是這張~

讓我覺得很奇怪的地方是罩抗,這里的CLEAR不是全透明的拉庵。我奇怪了很久google了很久也改代碼改了很久,確認我對CLEAR的理解沒有錯套蒂。最后發(fā)現(xiàn)了真相:

造成這個問題的原因是硬件加速钞支。需要在View中關閉硬件加速才能得到正確的結果。
比如在構造函數(shù)中調用:
setLayerType(View.LAYER_TYPE_SOFTWARE, null);

關閉硬件加速后操刀,就能生成正確的結果了烁挟。生成之后,我發(fā)現(xiàn)上張圖中的Darken和Lighten也是錯誤的喲骨坑。


參考資料:
click effect on button in Android
Porter/Duff Compositing and Blend Modes
PorterDuff.Mode
Alpha compositing
PorterduffXfermode: Clear a section of a bitmap
詳解Paint的setXfermode
Android example source code file (Xfermodes.java)

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末撼嗓,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子欢唾,更是在濱河造成了極大的恐慌且警,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件礁遣,死亡現(xiàn)場離奇詭異斑芜,居然都是意外死亡,警方通過查閱死者的電腦和手機祟霍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門杏头,熙熙樓的掌柜王于貴愁眉苦臉地迎上來盈包,“玉大人,你說我怎么就攤上這事醇王∧卦铮” “怎么了?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵寓娩,是天一觀的道長疮茄。 經(jīng)常有香客問我,道長根暑,這世上最難降的妖魔是什么力试? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮排嫌,結果婚禮上畸裳,老公的妹妹穿的比我還像新娘。我一直安慰自己淳地,他們只是感情好怖糊,可當我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著颇象,像睡著了一般伍伤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上遣钳,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天扰魂,我揣著相機與錄音,去河邊找鬼蕴茴。 笑死劝评,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的倦淀。 我是一名探鬼主播蒋畜,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼撞叽!你這毒婦竟也來了姻成?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤愿棋,失蹤者是張志新(化名)和其女友劉穎科展,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體初斑,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡辛润,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了见秤。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片砂竖。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖鹃答,靈堂內(nèi)的尸體忽然破棺而出乎澄,到底是詐尸還是另有隱情,我是刑警寧澤测摔,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布置济,位于F島的核電站,受9級特大地震影響锋八,放射性物質發(fā)生泄漏浙于。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一挟纱、第九天 我趴在偏房一處隱蔽的房頂上張望羞酗。 院中可真熱鬧,春花似錦紊服、人聲如沸檀轨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽参萄。三九已至,卻和暖如春煎饼,著一層夾襖步出監(jiān)牢的瞬間讹挎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工吆玖, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留淤袜,地道東北人。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓衰伯,卻偏偏與公主長得像铡羡,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子意鲸,可洞房花燭夜當晚...
    茶點故事閱讀 43,543評論 2 349

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