2011年11月24日 星期四

SurfaceView及Animation來實作圖片動畫

本篇討論運用SurfaceView及Animation來實作圖片動畫的差異性
Android 2.2
本篇不考慮非圖片式的動畫(如:動態畫點、線、面)
-----------------

名詞解說
SurfaceView:
SurfaceView本身是一個View物件,並提供canvas(畫布)來達成動畫的效果。一般來說,SurfaceView會搭配一個Thread來更新canvas。也就是,SurfaceView會透過畫布更新的方式來表現動畫。

Animation:
Animation本身不是一個View物件,因此必須搭配一個View(該View本身需指定Drawable物件)來達成動畫的效果。也就是,Animation是一種動畫效果,只要把該效果指定給某個View即可。

Animation又分Tween Animation及Frame Animation。
  • Tween Animation:宣告一個set的效果。在set裡包括alpha(透明度變化)、scale(大小變化)、translate(位置移動)及rotate(旋轉),該四種效果可分開或合併觸發,或是依時觸發。
  • Frame Animation:宣告一個animation-list,在list裡面則放入item,並指定item的drawable及持續時間。Android會依序程現item。

共同點/共通問題點:
資源管理:
主要的資源管理工作會花在Drawable的取用。因Drawable的資源名稱無法靠程式來正確取得,因此一般來說我會用HashMap的方式在程式開啟時開一條Thread將大部份的圖片載入並進行管理。

動畫流程計算及管理:
雖然Animation不需去計算動畫更新的細節,但仍可透過AnimationListener來管理動畫onAnimationStart(開始)/onAnimationEnd結束/onAnimationRepeat重覆事件。(注意:Frame Animation無法管理這些事件)。SurfaceView則必須實作動畫更新的所有細節。

人機互動 OnTouchListener/OnGestureListener事件 :
onTouchEvent(手指觸碰事件)及GestureDetector(手指滑動偵測)。SurfaceView本身是一個View因此可接收及實作此類事件,而Animation則要透過其他的View來接收事件(如主要的ContentView)。不管如何,在事件觸發後,都要實作相關的運算來決定動畫呈現的方式。

應用上如何取捨:
<在不考慮效能及動畫更新速度的情況下>
SurfaceView:
  • 動態更新的動畫。例如,圖片會隨著使用者的手部移動而移動。
  • 固定時間更新的動畫, 如:圖片隨時間改變。
  • 在更新過程中有邏輯判斷來決定更新的方式/內容
  • 遊戲類程式

Animation:
  • 固定展示的動畫。
  • 不想自行實作運算移動距離、時間及旋轉角度等問題時。
  • 想表現簡單的動畫在非遊戲類別的程式中

個人心得:
SurfaceView:
可控制的細節相當深入,因此可實現Animation做不到的變化,但其編碼較複雜,且要考慮canvas及thread的管理。若要和畫面上的其他View互動則只能透過callback或是傳送參考至SurfaceView的方式來進行操作。程式碼維護相對困難。處理surfaceCreated()、surfaceChanged()、surfaceDestroyed()事件時要相當小心,否則容易出錯在畫布更新。

因自由性相當高,若能進階使用GLSurfaceView(ex: 3D特效)則應可寫出任何想要的動畫效果。另外,雖說一個畫面中是可放入多個SurfaceView,但實作中發現thread會變的難以控制,尤其是SurfaceView(s)間還要互動時。因此建議擺一個即可。

小技巧:
  • 若在SurfaceView上還想放其他的View,但卻被背景遮著,可透過Paint.setXfermode(new PorterDuffXfermode(Mode.CLEAR))的方式來將SurfaceView的背景變透明。並設定
    SurfaceView.getHolder().setFormat(PixelFormat.RGBA_8888);
    SurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
    SurfaceView.setZOrderOnTop(true);

  • 每一張圖片的位置都要記錄並運用MotionEvent.getX()及getY()來判斷使用者是否有按到圖片,及按到的是那一張圖片。
  • 為了避免不同尺寸下,SurfaceView畫出來的圖片和其他的View位置會混亂,建議在onCreate()的時後去排定其他的View的大小及位置,而不是在xml檔案裡指定view的大小及位置。
  • doDraw中運用state-machine的方式來呈現動畫可減少許多的設計錯誤。記得要先畫state-machine的圖再實作。

Animation:
簡單,易用。

Animation可在xml中設定,也可以在程式中動態實作類別。但似乎無法在程式中讀入Animation xml資源再進行動態細節更改,目前只能更改set or animation-list的參數,尚未找到方法更改其包括其他node的參數(有待商確)。

動畫範圍和View設定的大小坐標有關,但可透過+/-的方式來讓動畫超出View的範圍(假設View比Activity畫面小,則超出的部份效果仍然可見)。

對一個ImageView來說,Frame Animation可設定在ImageView.setBackgroundDrawable(背景)或是ImageView.setImageDrawable(前景),效果相同,但若前景有圖片時,則背景效果不會出現。

Animation不能在onCreate()時啟動,因此時View的實作尚未完成。

若要將動畫運用在Activity切換,則:
Intent intent=new Intent(main.this,nextActivity.class);
startActivity
(intent);
overridePendingTransition
(R.anim.amFrom,R.anim.amTo);

被動畫效果移動的物件是不會停留在結束位置的。
即使
fillAfter="true"也只是視覺上的停留在該點,實際位置則是回到了初始值。


沒有留言:

張貼留言