閱讀906 返回首頁    go 技術社區[雲棲]


[Android] SurfaceView使用實例

https://blog.csdn.net/sodino/article/details/7704084

同樣,先上效果圖如下:


效果圖中,拋物線的動畫即是由SurfaceView實現的。底部欄中的文字翻轉詳情相關帖子:
[Android] 文字翻轉動畫的實現

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

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

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



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


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

[java] view plaincopy
  1. t1 = Math.sqrt(2 * height * 1.0d / GRAVITY);  

第一次與X軸碰撞後上升至最高點的耗時t2與高度 N%*height會有:

[java] view plaincopy
  1. t2 = Math.sqrt((1 - WASTAGE) * 2 * height * 1.0d / GRAVITY);  

那麼總的動畫時間為(t1 + t2 + t2),則水平位移速度有(width為X軸總位移):

[java] view plaincopy
  1. velocity = width * 1.0d / (t1 + 2 * t2);  

則根據時間計算圖片的實時坐標有:
PhysicalTool.comput()

[java] view plaincopy
  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,該回調提供了三個方法:

[java] view plaincopy
  1. surfaceCreated()//通知Surface已被創建,可以在此處啟動動畫線程  
  2. surfaceChanged()//通知Surface已改變  
  3. surfaceDestroyed()//通知Surface已被銷毀,可以在此處終止動畫線程  

SurfaceView使用有一個原則,即該界麵操作必須在surfaceCreated之後及surfaceDestroyed之前。該回調的監聽通過SurfaceHolder設置。代碼如下:
[java] view plaincopy
  1. //於SurfaceView類中,該類實現SurfaceHolder.Callback接口,如本例中的ParabolaView  
  2. SurfaceHolder holder = getHolder();  
  3. holder.addCallback(this);  

示例代碼中,通過啟動DrawThread調用handleThread()實現對SurfaceView的刷新。
        刷新界麵首先需要執行holder.lockCanvas()鎖定Canvas並獲得Canvas實例,然後進行界麵更新操作,最後結束鎖定Canvas,提交界麵更改,至Surface最終顯示在屏幕上。
        代碼如下:

[java] view plaincopy
  1. canvas = holder.lockCanvas();  
  2. … … … …   
  3. … … … …   
  4. canvas.drawBitmap(bitmap, x, y, paint);  
  5. holder.unlockCanvasAndPost(canvas);  


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

[java] view plaincopy
  1. canvas.drawColor(Color.TRANSPARENT, android.graphics.PorterDuff.Mode.CLEAR);  

對於SurfaceView的操作,下麵這個鏈接講述得更詳細,更易理解,推薦去看下:
Android開發之SurfaceView

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

本文由Sodino所有,轉載請注明出處:https://blog.csdn.net/sodino/article/details/7704084

[java] view plaincopy
  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. }  

[java] view plaincopy
  1. DrawThread.java  
  2.   
  3. package lab.sodino.surfaceview;  
  4.   
  5. import android.view.SurfaceView;  
  6.   
  7. /** 
  8.  * @author Sodino E-mail:sodinoopen@hotmail.com 
  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. }  

[java] view plaincopy
  1. ParabolaView.java  
  2. package lab.sodino.surfaceview;  
  3.   
  4. import android.content.Context;  
  5. import android.graphics.Bitmap;  
  6. import android.graphics.Canvas;  
  7. import android.graphics.Color;  
  8. import android.graphics.Paint;  
  9. import android.graphics.PixelFormat;  
  10. import android.util.AttributeSet;  
  11. import android.view.SurfaceHolder;  
  12. import android.view.SurfaceView;  
  13.   
  14. /** 
  15.  * @author Sodino E-mail:sodinoopen@hotmail.com 
  16.  * @version Time:2012-6-18 上午02:52:33 
  17.  */  
  18. public class ParabolaView extends SurfaceView implements SurfaceHolder.Callback {  
  19.         /** 每30ms刷一幀。 */  
  20.         private static final long SLEEP_DURATION = 10l;  
  21.         private SurfaceHolder holder;  
  22.         /** 動畫圖標。 */  
  23.         private Bitmap bitmap;  
  24.         private DrawThread thread;  
  25.         private PhysicalTool physicalTool;  
  26.         private ParabolaView.ParabolaListener listener;  
  27.         /** 默認未創建,相當於Destory。 */  
  28.         private boolean surfaceDestoryed = true;  
  29.   
  30.         public ParabolaView(Context context, AttributeSet attrs, int defStyle) {  
  31.                 super(context, attrs, defStyle);  
  32.                 init();  
  33.         }  
  34.   
  35.         public ParabolaView(Context context, AttributeSet attrs) {  
  36.                 super(context, attrs);  
  37.                 init();  
  38.         }  
  39.   
  40.         public ParabolaView(Context context) {  
  41.                 super(context);  
  42.                 init();  
  43.         }  
  44.   
  45.         private void init() {  
  46.                 holder = getHolder();  
  47.                 holder.addCallback(this);  
  48.                 holder.setFormat(PixelFormat.TRANSPARENT);  
  49.   
  50.                 setZOrderOnTop(true);  
  51.                 // setZOrderOnTop(false);  
  52.   
  53.                 physicalTool = new PhysicalTool();  
  54.         }  
  55.   
  56.         @Override  
  57.         public void surfaceCreated(SurfaceHolder holder) {  
  58.                 surfaceDestoryed = false;  
  59.         }  
  60.   
  61.         @Override  
  62.         public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {  
  63.   
  64.         }  
  65.   
  66.         @Override  
  67.         public void surfaceDestroyed(SurfaceHolder holder) {  
  68.                 LogOut.out(this"surfaceDestroyed");  
  69.                 surfaceDestoryed = true;  
  70.                 physicalTool.cancel();  
  71.         }  
  72.   
  73.         public void handleThread() {  
  74.                 Canvas canvas = null;  
  75.   
  76.                 Paint pTmp = new Paint();  
  77.                 pTmp.setAntiAlias(true);  
  78.                 pTmp.setColor(Color.RED);  
  79.   
  80.                 Paint paint = new Paint();  
  81.                 // 設置抗鋸齒  
  82.                 paint.setAntiAlias(true);  
  83.                 paint.setColor(Color.CYAN);  
  84.                 physicalTool.start();  
  85.                 LogOut.out(this"doing:" + physicalTool.doing());  
  86.                 if (listener != null) {  
  87.                         listener.onParabolaStart(this);  
  88.                 }  
  89.                 while (physicalTool.doing()) {  
  90.                         try {  
  91.                                 physicalTool.compute();  
  92.                                 canvas = holder.lockCanvas();  
  93.                                 // 設置畫布的背景為透明。  
  94.                                 canvas.drawColor(Color.TRANSPARENT, android.graphics.PorterDuff.Mode.CLEAR);  
  95.                                 // 繪上新圖區域  
  96.                                 float x = (float) physicalTool.getX();  
  97.                                 // float y = (float) physicalTool.getY();  
  98.                                 float y = (float) physicalTool.getMirrorY(getHeight(), bitmap.getHeight());  
  99.                                 // LogOut.out(this, "x:" + x + " y:" + y);  
  100.                                 canvas.drawRect(x, y, x + bitmap.getWidth(), y + bitmap.getHeight(), pTmp);  
  101.                                 canvas.drawBitmap(bitmap, x, y, paint);  
  102.                                 holder.unlockCanvasAndPost(canvas);  
  103.                                 Thread.sleep(SLEEP_DURATION);  
  104.                         } catch (Exception e) {  
  105.                                 e.printStackTrace();  
  106.                         }  
  107.                 }  
  108.                 // 清除屏幕內容  
  109.                 // 直接按"Home"回桌麵,SurfaceView被銷毀了,lockCanvas返回為null。  
  110.                 if (surfaceDestoryed == false) {  
  111.                         canvas = holder.lockCanvas();  
  112.                         canvas.drawColor(Color.TRANSPARENT, android.graphics.PorterDuff.Mode.CLEAR);  
  113.                         holder.unlockCanvasAndPost(canvas);  
  114.                 }  
  115.   
  116.                 thread.setRunning(false);  
  117.                 if (listener != null) {  
  118.                         listener.onParabolaEnd(this);  
  119.                 }  
  120.         }  
  121.   
  122.         public void showMovie() {  
  123.                 if (thread == null) {  
  124.                         thread = new DrawThread(this);  
  125.                 } else if (thread.getState() == Thread.State.TERMINATED) {  
  126.                         thread.setRunning(false);  
  127.                         thread = new DrawThread(this);  
  128.                 }  
  129.                 LogOut.out(this"thread.getState:" + thread.getState());  
  130.                 if (thread.getState() == Thread.State.NEW) {  
  131.                         thread.start();  
  132.                 }  
  133.         }  
  134.   
  135.         /** 正在播放動畫時,返回true;否則返回false。 */  
  136.         public boolean isShowMovie() {  
  137.                 return physicalTool.doing();  
  138.         }  
  139.   
  140.         public void setIcon(Bitmap bit) {  
  141.                 bitmap = bit;  
  142.         }  
  143.   
  144.         public void setParams(int height, int width) {  
  145.                 physicalTool.setParams(height, width);  
  146.         }  
  147.   
  148.         /** 設置拋物線的動畫監聽器。 */  
  149.         public void setParabolaListener(ParabolaView.ParabolaListener listener) {  
  150.                 this.listener = listener;  
  151.         }  
  152.   
  153.         static interface ParabolaListener {  
  154.                 public void onParabolaStart(ParabolaView view);  
  155.   
  156.                 public void onParabolaEnd(ParabolaView view);  
  157.         }  
  158. }  


[java] view plaincopy
  1. PhysicalTool.java  
  2. package lab.sodino.surfaceview;  
  3.   
  4. /** 
  5.  * @author Sodino E-mail:sodinoopen@hotmail.com 
  6.  * @version Time:2012-6-18 上午06:07:16 
  7.  */  
  8. public class PhysicalTool {  
  9.         /** 重力加速度值。 */  
  10.         private static final float GRAVITY = 400.78033f;  
  11.         /** 與X軸碰撞後,重力勢能損失掉的百分比。 */  
  12.         private static final float WASTAGE = 0.3f;  
  13.         /** 起始下降高度。 */  
  14.         private int height;  
  15.         /** 起始點到終點的X軸位移。 */  
  16.         private int width;  
  17.         /** 水平位移速度。 */  
  18.         private double velocity;  
  19.         /** X Y坐標。 */  
  20.         private double x, y;  
  21.         /** 動畫開始時間。 */  
  22.         private long startTime;  
  23.         /** 首階段下載的時間。 單位:毫秒。 */  
  24.         private double t1;  
  25.         /** 第二階段上升與下載的時間。 單位:毫秒。 */  
  26.         private double t2;  
  27.         /** 動畫正在進行時值為true,反之為false。 */  
  28.         private boolean doing;  
  29.   
  30.         public void start() {  
  31.                 startTime = System.currentTimeMillis();  
  32.                 doing = true;  
  33.         }  
  34.   
  35.         /** 設置起始下落的高度及水平初速度;並以此計算小球下落的第一階段及第二階段上升耗時。 */  
  36.         public void setParams(int h, int w) {  
  37.                 height = h;  
  38.                 width = w;  
  39.   
  40.                 t1 = Math.sqrt(2 * height * 1.0d / GRAVITY);  
  41.                 t2 = Math.sqrt((1 - WASTAGE) * 2 * height * 1.0d / GRAVITY);  
  42.                 velocity = width * 1.0d / (t1 + 2 * t2);  
  43.                 LogOut.out(this"t1=" + t1 + " t2=" + t2);  
  44.         }  
  45.   
  46.         /** 根據當前時間計算小球的X/Y坐標。 */  
  47.         public void compute() {  
  48.                 double used = (System.currentTimeMillis() - startTime) * 1.0d / 1000;  
  49.                 x = velocity * used;  
  50.                 if (0 <= used && used < t1) {  
  51.                         y = height - 0.5d * GRAVITY * used * used;  
  52.                 } else if (t1 <= used && used < (t1 + t2)) {  
  53.                         double tmp = t1 + t2 - used;  
  54.                         y = (1 - WASTAGE) * height - 0.5d * GRAVITY * tmp * tmp;  
  55.                 } else if ((t1 + t2) <= used && used < (t1 + 2 * t2)) {  
  56.                         double tmp = used - t1 - t2;  
  57.                         y = (1 - WASTAGE) * height - 0.5d * GRAVITY * tmp * tmp;  
  58.                 } else {  
  59.                         LogOut.out(this"used:" + used + " set doing false");  
  60.                         x = velocity * (t1 + 2 * t2);  
  61.                         y = 0;  
  62.                         doing = false;  
  63.                 }  
  64.         }  
  65.   
  66.         public double getX() {  
  67.                 return x;  
  68.         }  
  69.   
  70.         public double getY() {  
  71.                 return y;  
  72.         }  
  73.   
  74.         /** 反轉Y軸正方向。適應手機的真實坐標係。 */  
  75.         public double getMirrorY(int parentHeight, int bitHeight) {  
  76.                 int half = parentHeight >> 1;  
  77.                 double tmp = half + (half - y);  
  78.                 tmp -= bitHeight;  
  79.                 return tmp;  
  80.         }  
  81.   
  82.         public boolean doing() {  
  83.                 return doing;  
  84.         }  
  85.   
  86.         public void cancel() {  
  87.                 doing = false;  
  88.         }  
  89. }  

[java] view plaincopy
  1. RotateAnimation.java  
  2. package lab.sodino.surfaceview;  
  3.   
  4. import android.graphics.Camera;  
  5. import android.graphics.Matrix;  
  6. import android.view.animation.Animation;  
  7. import android.view.animation.Transformation;  
  8.   
  9. /** 
  10.  * @author Sodino E-mail:sodinoopen@hotmail.com 
  11.  * @version Time:2012-6-27 上午07:32:00 
  12.  */  
  13. public class RotateAnimation extends Animation {  
  14.         /** 值為true時可明確查看動畫的旋轉方向。 */  
  15.         public static final boolean DEBUG = false;  
  16.         /** 沿Y軸正方向看,數值減1時動畫逆時針旋轉。 */  
  17.         public static final boolean ROTATE_DECREASE = true;  
  18.         /** 沿Y軸正方向看,數值減1時動畫順時針旋轉。 */  
  19.         public static final boolean ROTATE_INCREASE = false;  
  20.         /** Z軸上最大深度。 */  
  21.         public static final float DEPTH_Z = 310.0f;  
  22.         /** 動畫顯示時長。 */  
  23.         public static final long DURATION = 800l;  
  24.         /** 圖片翻轉類型。 */  
  25.         private final boolean type;  
  26.         private final float centerX;  
  27.         private final float centerY;  
  28.         private Camera camera;  
  29.         /** 用於監聽動畫進度。當值過半時需更新txtNumber的內容。 */  
  30.         private InterpolatedTimeListener listener;  
  31.   
  32.         public RotateAnimation(float cX, float cY, boolean type) {  
  33.                 centerX = cX;  
  34.                 centerY = cY;  
  35.                 this.type = type;  
  36.                 setDuration(DURATION);  
  37.         }  
  38.   
  39.         public void initialize(int width, int height, int parentWidth, int parentHeight) {  
  40.                 // 在構造函數之後、getTransformation()之前調用本方法。  
  41.                 super.initialize(width, height, parentWidth, parentHeight);  
  42.                 camera = new Camera();  
  43.         }  
  44.   
  45.         public void setInterpolatedTimeListener(InterpolatedTimeListener listener) {  
  46.                 this.listener = listener;  
  47.         }  
  48.   
  49.         protected void applyTransformation(float interpolatedTime, Transformation transformation) {  
  50.                 // interpolatedTime:動畫進度值,範圍為[0.0f,10.f]  
  51.                 if (listener != null) {  
  52.                         listener.interpolatedTime(interpolatedTime);  
  53.                 }  
  54.                 float from = 0.0f, to = 0.0f;  
  55.                 if (type == ROTATE_DECREASE) {  
  56.                         from = 0.0f;  
  57.                         to = 180.0f;  
  58.                 } else if (type == ROTATE_INCREASE) {  
  59.                         from = 360.0f;  
  60.                         to = 180.0f;  
  61.                 }  
  62.                 float degree = from + (to - from) * interpolatedTime;  
  63.                 boolean overHalf = (interpolatedTime > 0.5f);  
  64.                 if (overHalf) {  
  65.                         // 翻轉過半的情況下,為保證數字仍為可讀的文字而非鏡麵效果的文字,需翻轉180度。  
  66.                         degree = degree - 180;  
  67.                 }  
  68.                 // float depth = 0.0f;  
  69.                 float depth = (0.5f - Math.abs(interpolatedTime - 0.5f)) * DEPTH_Z;  
  70.                 final Matrix matrix = transformation.getMatrix();  
  71.                 camera.save();  
  72.                 camera.translate(0.0f, 0.0f, depth);  
  73.                 camera.rotateY(degree);  
  74.                 camera.getMatrix(matrix);  
  75.                 camera.restore();  
  76.                 if (DEBUG) {  
  77.                         if (overHalf) {  
  78.                                 matrix.preTranslate(-centerX * 2, -centerY);  
  79.                                 matrix.postTranslate(centerX * 2, centerY);  
  80.                         }  
  81.                 } else {  
  82.                         matrix.preTranslate(-centerX, -centerY);  
  83.                         matrix.postTranslate(centerX, centerY);  
  84.                 }  
  85.         }  
  86.   
  87.         /** 動畫進度監聽器。 */  
  88.         public static interface InterpolatedTimeListener {  
  89.                 public void interpolatedTime(float interpolatedTime);  
  90.         }  
  91. }  

最後更新:2017-04-03 18:51:44

  上一篇:go Android-SurfaceView與SurfaceHolder對象
  下一篇:go iOS網絡編程-iCloud文檔存儲編程實例