Android开发教程:SurfaceView使用实例


同样,先上效果图如下:


效果图中,抛物线的动画即是由SurfaceView实现的。底部栏中的文字翻转详情相关帖子:
Android开发教程:文字翻转动画的实现 

需求:
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

惯例,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: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. }  
  • 1
  • 2
  • 下一页

相关内容