歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Android開發教程:SurfaceView使用實例

Android開發教程:SurfaceView使用實例

日期:2017/3/1 10:18:34   编辑:Linux編程

同樣,先上效果圖如下:


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