Android圖形動畫基礎

來源:互聯網
上載者:User

標籤: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, 00null);             invalidate();        }        ...    }}

有了不斷變化的ratio變數,繪圖級動畫就可以大展身手了。

繪圖級動畫的強大能力來自繪圖API的強大能力,下面主要講繪圖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圖形動畫基礎

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.