某天肥卡,設計師同學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)在我們把紅色的圓形這張圖放在下面肛炮,將藍色的方形這張圖疊上去。這時,每一個像素點的位置可能會有四種情況:
- 這兩張圖片侨糟,在這一個位置上的像素點都是透明碍扔;
- 這兩張圖片,在這一個位置上的像素點都是不透明的秕重;
- 紅色的圓形圖片中不同,在這個位置的像素點是透明的,而藍色的方形圖片中溶耘,在這個位置上的像素點是不透明的二拐;
- 藍色的方形圖片中,在這個位置的像素點是透明的凳兵,而紅色的圓形圖片中卓鹿,在這個位置上的像素點是不透明的;
那么這兩張圖片上下疊起來之后留荔,最后呈現(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 |
畫圖直觀感受一下:
感受就是媚狰,這好像和“邏輯運算”有著隱隱的關系-.-岛杀,這里DST代表了壓在下面的圖片,比如例子中的紅色的圓形圖片崭孤,SRC代表了疊在上面的圖片类嗤,比如例子中的藍色的方形圖片衫生。
好啦,那么在alpha可以是[0, 1]中的任意值的情況下呢土浸?
現(xiàn)在我們讓紅色的圓形和藍色的方形的都變成半透明的:
先用這張圖直觀的感受一下:
能發(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:
- 兩張圖“重疊部分”之外的顏色,要么是透明达吞,要么就是原來的顏色张弛;
- CLEAR模式下,整張圖都是透明的酪劫;
- SRC模式下得到的圖吞鸭,就是SRC;DST模式下得到的圖覆糟,就是DST刻剥;
對了,除了這12中模式搪桂,PorterDuff中還剩下的6種模式(ADD透敌、DARKEN盯滚、LIGHTEN踢械、MULTIPLY、OVERLAY魄藕、SCREEN)是什么呢内列?
如果我沒理解錯的話,這6種模式得到的效果背率,和Photoshop里對應的混合模式得到的效果是一致的(好像MULTIPLY是不完全一致0.0)话瞧。
效果:
終于把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;
}
});
效果:
寫這篇文章時遇到的一個問題:
文章中紅色圓形和藍色方塊的例子委刘,是我在安卓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)