標籤:android blog http io ar color os 使用 sp
本質
每幀繪製不同的內容。
基本過程
開始動畫後,調用View的invalidate觸發重繪。重繪後檢查動畫是否停止,若未停止則繼續調用invalidate觸發下一幀(下一次重繪),直到動畫結束。
重繪時View的draw方法會被調用,根據動畫的進行繪製不同的內容,如某個被繪製元素的大小變化、角度旋轉、透明度變化等,這樣即會產生動畫。
動畫的推進過程一般都會有一個變化量,這個變數會被用到draw方法內元素的繪製。一般的變數都是時間,也可以是手指移動、感應器等任何其他的變數。
Android中的動畫支援
Animation:早期實現的讓View整體做動畫的類。能讓View做Matrix(移動、縮放、旋轉、3D旋轉)和Alpha(透明)的動畫。
Animator:有硬體加速後為做動畫實現的類。能方便的讓View整體做動畫;也可以只產生隨時間變化的變數,用來在onDraw裡做繪圖級的動畫。比Animation靈活很多。
AnimationDrawable:圖片逐幀動畫。主要用來播放提前製作好的動畫。
在哪個層級做動畫
讓整個View做動畫(比如整個View平移、旋轉等)很簡單方便,一般調用幾行代碼就行。我把它稱作View級的動畫。
在View的draw/onDraw裡通過Canvas來繪製時做動畫更靈活,更精細,能力更強大。我把它稱作繪圖級的動畫。(View級的動畫本質上也是這麼做的,只是Android系統幫我們做了大部分工作)
繪圖級的動畫
這篇文章主要講繪圖級的動畫。
下面來一段繪圖級動畫的典型實現:
class MyView extends View { void startAnimator() { ValueAnimator animator = ValueAnimator.ofFloat(1f, 0f); animator.start(); invalidate(); } protected void onDraw(Canvas canvas) { if (animator.isRunning()) { float ratio = (Float)animator.getAnimatedValue(); canvas.rotate(ratio*360); canvas.drawBitmap(bitmap, 0, 0, null); invalidate(); } ... }} |
有了不斷變化的ratio變數,繪圖級動畫就可以大展身手了。
繪圖級動畫的強大能力來自繪圖API的強大能力,下面主要講繪圖API。
Matrix
Canvas.[translate,scale,rotate,skew]方法
Matrix.set/pre/post[translate,scale,rotate,skew]方法
平移、縮放、旋轉、斜切。
從使用API的角度來看,我們通過調用Canvas.translate等方法,可以使後續在此Canvas上繪製操作的繪製地區變化,如translate(5,0),則後續所有繪製操作的繪製地區都會向右移動5個像素。
原理:Canvas裡有一個Matrix,Canvas上的這幾個調用都會最終調用到Matrix.pre*。這個Matrix儲存整個變換過程。當有Canvas.draw時,要繪製的點都會經過Matrix.mapPoints方法做一個映射。於是產生我們期望的變換效果。(事實上映射的時候只需要映射關鍵點,其他的是插值來的)
關於Matrix的更多資訊
set/pre/post的區別:set是設定,衝掉以前的資料。pre是前乘,post是後乘,根本上講就是生效順序不同。具體表現效果可在網上搜尋資料。
setPolyToPoly:與mapPoints方法相反,mapPoints是通過矩陣把原始點映射為目標點。setPolyToPoly是輸入原始點和映射後的目標點,計算出這個矩陣。
Camera:有透視效果的3D旋轉。Camera是一個產生Matrix的工具類。可用來產生有透視效果的3D旋轉。
Canvas.draw*方法
Canvas.draw-Point/s
Canvas.draw-Line/s
Canvas.draw-Rect,RoundRect,Circle,Oval,Arc,Path
Canvas.draw-Text
Canvas.draw-Bitmap,BitmapMesh
Canvas.draw-Color,Paint
這些方法都表示繪製一個地區。繪製的地區中究竟填充什麼顏色,由Paint決定。
Color,Paint,Bitmap,BitmapMesh這幾個則除了指定繪製地區外,還指定了填充內容。
Path功能比較強大,可自行組織成任何形狀,還可以用貝茲路徑。
這些方法基本上都很好理解,從名字上即可看出其功能。這裡重點提一下drawBitmapMesh。
drawBitmapMesh是輸入一個網格模型,繪製出的圖片內容將依據這個網格來扭曲。可以想像成把圖片畫在一塊有彈性的布上,當我們把布的某些地區扯動的時候,會形成畫面扭曲效果。
樣本:假設有個30x30大小的圖片,我們建立這樣的網格輸入:
0,0, 15,0, 30,0,
0,15, 15,15, 30,15,
0,30, 15,30, 30,30
則圖片會原樣輸出,沒有任何扭曲。
如果我們建立這樣的網格輸入:
0,0, 15,12, 30,0,
0,15, 15,15, 30,15,
0,30, 15,30, 30,30
則原本[15,0]的點會被繪製到[15,12]的位置上去。圖片繪製出來後,上面部分會缺一塊,形成用手把圖片從上邊中間位置往下拉的扭曲效果。但很銳利,上面缺的一塊是個三角形而不會是半圓型,通常我們希望的是半圓型,這就需要我們把這個網格建得密一些。
Alpha通道
每個Color裡可以有四個通道ARGB,其中RGB是紅綠藍,A即Alpha通道,它通常的作用是用來作為此顏色的透明度。
因為我們的顯示屏是沒法透明的,因此最終顯示在螢幕上的顏色裡可以認為沒有Alpha通道。Alpha通道主要在兩個映像混合的時候生效。
預設情況下,當一個顏色繪製到Canvas上時的混合模式是這樣計算的:(RGB通道) 最終顏色 = 繪製的顏色 + (1 - 繪製顏色的透明度) × Canvas上的原有顏色。
注意:
1.這裡我們一般把每個通道的取值從0到255映射到0到1的浮點數表示。
2.這裡等式右邊的“繪製的顏色"、“Canvas上的原有顏色”都是經過預乘了自己的Alpha通道的值。如繪製顏色:0x88ffffff,那麼參與運算時的每個色彩通道的值不是1.0,而是(1.0 * 0.53125 = 0.53125)。
使用這種方式的混合,就會造成後繪製的內容以半透明的方式疊在上面的視覺效果。
其實還可以有不同的混合模式供我們選擇,用Paint.setXfermode,指定不同的PorterDuff.Mode。
下表是各個PorterDuff模式的混合計算公式:(D指原本在Canvas上的內容dst,S指繪製輸入的內容src,a指alpha通道,c指RGB各個通道)
| ADD |
Saturate(S + D) |
| CLEAR |
[0, 0] |
| DARKEN |
[Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)] |
| DST |
[Da, Dc] |
| DST_ATOP |
[Sa, Sa * Dc + Sc * (1 - Da)] |
| DST_IN |
[Sa * Da, Sa * Dc] |
| DST_OUT |
[Da * (1 - Sa), Dc * (1 - Sa)] |
| DST_OVER |
[Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc] |
| LIGHTEN |
[Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)] |
| MULTIPLY |
[Sa * Da, Sc * Dc] |
| SCREEN |
[Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] |
| SRC |
[Sa, Sc] |
| SRC_ATOP |
[Da, Sc * Da + (1 - Sa) * Dc] |
| SRC_IN |
[Sa * Da, Sc * Da] |
| SRC_OUT |
[Sa * (1 - Da), Sc * (1 - Da)] |
| SRC_OVER |
[Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] |
| XOR |
[Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc] |
可以發現,我們之前的預設混合模式其實就是SRC_OVER。
通過選擇其他的PorterDuff模式,我們可以達到一些特殊的效果:
使用DST_OVER的話,相當於後繪製的內容作為背景在底下。
使用DST_IN/DST_OUT的話,可以裁剪Canvas裡的內容,或用一張帶alpha的圖片mask指定哪些地區顯示/不顯示。
通過選擇SRC_ATOP可以只在Canvas上有內容(不透明)的地方繪製。
用一張樣本圖來查看使用不同模式時的混合效果(src表示輸入的圖,dst表示原Canvas上的內容):
填充顏色
之前說過Canvas.draw*指定了繪製的地區。而地區裡的填充顏色是由Paint來指定的。
Paint.setColor指定純色。
Paint.setShader可指定:BitmapShader, LinearGradient, RadialGradient, SweepGradient, ComposeShader。
BitmapShader:圖片填充。
LinearGradient, RadialGradient, SweepGradient:漸層填充。
ComposeShader:疊加前面的某兩種。可選擇PorterDuff混合模式。
如果既調用了setColor,又調用了setShader,則setShader生效。如果同時用setColor或setAlpha設定了透明度,則透明度也會生效。(會和Shader的透明度疊加)
如果使用drawBitmap輸入一個只有alpha的圖片(可用Bitmap.extractAlpha方法獲得),則會以alpha圖片為mask,繪製出shader/color的顏色。
ColorFilter
通過ColorFilter可以對一次繪製的所有像素做一個通用處理。
Paint.setColorFilter: LightingColorFilter, PorterDuffColorFilter, ColorMatrixColorFilter。
這可以整體上改變這一次draw的內容,比如讓顏色更暗、更亮等。
這裡重點介紹下ColorMatrixColorFilter。
ColorMatrix是4x5矩陣,定義其每個元素如下:
{ a, b, c, d, e,
f, g, h, i, j,
k, l, m, n, o,
p, q, r, s, t }
則ColorMatrix的最終運算方式如下:
R‘ = a*R + b*G + c*B + d*A + e;
G‘ = f*R + g*G + h*B + i*A + j;
B‘ = k*R + l*G + m*B + n*A + o;
A‘ = p*R + q*G + r*B + s*A + t;
可在這裡方便的實驗flash線上版。
繪圖API架構
整個繪製流水線大概如下:(我們能定義的部分用藍色表示)
考慮動畫實現的時候一般從兩個角度來思考:
宏觀角度:有幾個變化量,分別是什麼。動畫從開始到結束的流程。
微觀角度:從某一幀上去想,在變化量為某個數值時的映像,該怎麼繪製。
把這兩者分開去想,就會比較清晰。
PPT裡有樣本,可以參照DEMO來熟悉:
怎麼做動畫.ppt
Android圖形系統簡介.ppt
GraphicsDemo.apk
GraphicsDemo_src.zip
Android圖形動畫基礎