歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Android開發教程:SurfaceView使用實例

同樣,先上效果圖如下:


效果圖中,拋物線的動畫即是由SurfaceView實現的。底部欄中的文字翻轉詳情相關帖子:
Android開發教程:文字翻轉動畫的實現 http://www.linuxidc.com/Linux/2012-06/64051.htm

需求:
1.實現拋物線動畫
   1.1 設計物理模型,能夠根據時間變量計算出某個時刻圖片的X/Y坐標。
   1.2 將圖片高頻率(相比於UI線程的緩慢而言)刷新到界面中。這兒需要實現將髒界面清屏及刷新操作。
2.文字翻轉動畫(已解決,見上面的帖子鏈接)

下面來逐一解決所提出的問題。

-----------------------------------------------------------------------------
分隔線內容與Android無關,請慎讀,勿拍磚。謝啦

1.1 設計物理模型,如果大家還記得初中物理時,這並不難。自己寫的草稿圖見下:


可以有:圖片要從高度為H的位置下落,並且第一次與X軸碰撞時會出現能量損失,至原來的N%。並且我們需要圖片的最終落點離起始位置在X軸上的位移為L,默認存在重力加速度g。
詳細的物理分析見上圖啦,下面只說代碼中如何實現,相關代碼在PhysicalTool.java。
第一次下落過程所耗時t1與高度height會有如下關系:

  1. t1 = Math.sqrt(2 * height * 1.0d / GRAVITY);  
第一次與X軸碰撞後上升至最高點的耗時t2與高度 N%*height會有:
  1. t2 = Math.sqrt((1 - WASTAGE) * 2 * height * 1.0d / GRAVITY);  
那麼總的動畫時間為(t1 + t2 + t2),則水平位移速度有(width為X軸總位移):
  1. velocity = width * 1.0d / (t1 + 2 * t2);  
則根據時間計算圖片的實時坐標有:
PhysicalTool.comput()
  1. double used = (System.currentTimeMillis() - startTime) * 1.0d / 1000;  
  2. x = velocity * used;  
  3. if (0 <= used && used < t1) {  
  4.         y = height - 0.5d * GRAVITY * used * used;  
  5. else if (t1 <= used && used < (t1 + t2)) {  
  6.         double tmp = t1 + t2 - used;  
  7.         y = (1 - WASTAGE) * height - 0.5d * GRAVITY * tmp * tmp;  
  8. else if ((t1 + t2) <= used && used < (t1 + 2 * t2)) {  
  9.         double tmp = used - t1 - t2;  
  10.         y = (1 - WASTAGE) * height - 0.5d * GRAVITY * tmp * tmp;  
  11. }  
Android無關內容結束了。
----------------------------------------------------------------------------------------

1.2 SurfaceView刷新界面
        SurfaceView是一個特殊的UI組件,特殊在於它能夠使用非UI線程刷新界面。至於為何具有此特殊性,將在另一個帖子"SurfaceView 相關知識筆記"中討論,該帖子將講述SurfaceView、Surface、ViewRoot、Window Manager/Window、Canvas等之間的關系。
        
使用SurfaceView需要自定義組件繼承該類,並實現SurfaceHolder.Callback,該回調提供了三個方法:
  1. surfaceCreated()//通知Surface已被創建,可以在此處啟動動畫線程   
  2. surfaceChanged()//通知Surface已改變   
  3. surfaceDestroyed()//通知Surface已被銷毀,可以在此處終止動畫線程  
SurfaceView使用有一個原則,即該界面操作必須在surfaceCreated之後及surfaceDestroyed之前。該回調的監聽通過SurfaceHolder設置。代碼如下:
  1. //於SurfaceView類中,該類實現SurfaceHolder.Callback接口,如本例中的ParabolaView   
  2. SurfaceHolder holder = getHolder();  
  3. holder.addCallback(this);  
示例代碼中,通過啟動DrawThread調用handleThread()實現對SurfaceView的刷新。
        刷新界面首先需要執行holder.lockCanvas()鎖定Canvas並獲得Canvas實例,然後進行界面更新操作,最後結束鎖定Canvas,提交界面更改,至Surface最終顯示在屏幕上。
        代碼如下:
  1. canvas = holder.lockCanvas();  
  2. … … … …   
  3. … … … …   
  4. canvas.drawBitmap(bitmap, x, y, paint);  
  5. holder.unlockCanvasAndPost(canvas);  

本例中,需要清除屏幕髒區域,出於簡便的做法,是將整個SurfaceView背景重復地設置為透明,代碼為:

  1. canvas.drawColor(Color.TRANSPARENT, android.graphics.PorterDuff.Mode.CLEAR);  
對於SurfaceView的操作,下面這個鏈接講述得更詳細,更易理解,推薦去看下:
Android開發之SurfaceView http://www.linuxidc.com/Linux/2012-06/64052.htm

慣例,Java代碼如下,XML請自行實現

  1. ActSurfaceView.java  
  2.   
  3. package lab.sodino.surfaceview;  
  4.   
  5. import lab.sodino.surfaceview.RotateAnimation.InterpolatedTimeListener;  
  6. import android.app.Activity;  
  7. import android.graphics.BitmapFactory;  
  8. import android.os.Bundle;  
  9. import android.os.Handler;  
  10. import android.os.Handler.Callback;  
  11. import android.os.Message;  
  12. import android.view.View;  
  13. import android.view.View.OnClickListener;  
  14. import android.view.ViewGroup;  
  15. import android.widget.Button;  
  16. import android.widget.TextView;  
  17.   
  18. public class ActSurfaceView extends Activity implements OnClickListener, ParabolaView.ParabolaListener, Callback,  
  19.                 InterpolatedTimeListener {  
  20.         public static final int REFRESH_TEXTVIEW = 1;  
  21.         private Button btnStartAnimation;  
  22.         /** 動畫界面。 */  
  23.         private ParabolaView parabolaView;  
  24.         /** 購物車處顯示購物數量的TextView。 */  
  25.         private TextView txtNumber;  
  26.         /** 購物車中的數量。 */  
  27.         private int number;  
  28.         private Handler handler;  
  29.         /** TextNumber是否允許顯示最新的數字。 */  
  30.         private boolean enableRefresh;  
  31.   
  32.         public void onCreate(Bundle savedInstanceState) {  
  33.                 super.onCreate(savedInstanceState);  
  34.                 setContentView(R.layout.main);  
  35.   
  36.                 handler = new Handler(this);  
  37.   
  38.                 number = 0;  
  39.   
  40.                 btnStartAnimation = (Button) findViewById(R.id.btnStartAnim);  
  41.                 btnStartAnimation.setOnClickListener(this);  
  42.   
  43.                 parabolaView = (ParabolaView) findViewById(R.id.surfaceView);  
  44.                 parabolaView.setParabolaListener(this);  
  45.   
  46.                 txtNumber = (TextView) findViewById(R.id.txtNumber);  
  47.         }  
  48.   
  49.         public void onClick(View v) {  
  50.                 if (v == btnStartAnimation) {  
  51.                         LogOut.out(this"isShowMovie:" + parabolaView.isShowMovie());  
  52.                         if (parabolaView.isShowMovie() == false) {  
  53.                                 number++;  
  54.                                 enableRefresh = true;  
  55.                                 parabolaView.setIcon(BitmapFactory.decodeResource(getResources(), R.drawable.icon));  
  56.                                 // 設置起始Y軸高度和終止X軸位移   
  57.                                 parabolaView.setParams(200, ((ViewGroup) txtNumber.getParent()).getLeft());  
  58.                                 parabolaView.showMovie();  
  59.                         }  
  60.                 }  
  61.         }  
  62.   
  63.         public void onParabolaStart(ParabolaView view) {  
  64.   
  65.         }  
  66.   
  67.         public void onParabolaEnd(ParabolaView view) {  
  68.                 handler.sendEmptyMessage(REFRESH_TEXTVIEW);  
  69.         }  
  70.   
  71.         public boolean handleMessage(Message msg) {  
  72.                 switch (msg.what) {  
  73.                 case REFRESH_TEXTVIEW:  
  74.   
  75.                         if (txtNumber.getVisibility() != View.VISIBLE) {  
  76.                                 txtNumber.setVisibility(View.VISIBLE);  
  77.                         }  
  78.                         RotateAnimation anim = new RotateAnimation(txtNumber.getWidth() >> 1, txtNumber.getHeight() >> 1,  
  79.                                         RotateAnimation.ROTATE_INCREASE);  
  80.                         anim.setInterpolatedTimeListener(this);  
  81.                         txtNumber.startAnimation(anim);  
  82.                         break;  
  83.                 }  
  84.                 return false;  
  85.         }  
  86.   
  87.         @Override  
  88.         public void interpolatedTime(float interpolatedTime) {  
  89.                 // 監聽到翻轉進度過半時,更新txtNumber顯示內容。   
  90.                 if (enableRefresh && interpolatedTime > 0.5f) {  
  91.                         txtNumber.setText(Integer.toString(number));  
  92.                         // Log.d("ANDROID_LAB", "setNumber:" + number);   
  93.                         enableRefresh = false;  
  94.                 }  
  95.         }  
  96. }  
  1. DrawThread.java  
  2.   
  3. package lab.sodino.surfaceview;  
  4.   
  5. import android.view.SurfaceView;  
  6.   
  7. /** 
  8.  * @author Sodino E-mail:[email protected] 
  9.  * @version Time:2012-6-18 上午03:14:31 
  10.  */  
  11. public class DrawThread extends Thread {  
  12.         private SurfaceView surfaceView;  
  13.         private boolean running;  
  14.   
  15.         public DrawThread(SurfaceView surfaceView) {  
  16.                 this.surfaceView = surfaceView;  
  17.         }  
  18.   
  19.         public void run() {  
  20.                 if (surfaceView == null) {  
  21.                         return;  
  22.                 }  
  23.                 if (surfaceView instanceof ParabolaView) {  
  24.                         ((ParabolaView) surfaceView).handleThread();  
  25.                 }  
  26.         }  
  27.   
  28.         public void setRunning(boolean b) {  
  29.                 running = b;  
  30.         }  
  31.   
  32.         public boolean isRunning() {  
  33.                 return running;  
  34.         }  
  35. }  
Copyright © Linux教程網 All Rights Reserved