Android SurfaceView画抛物线

转自:http://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会有如下关系:

  1. t1 = Math.sqrt(2 * height * 1.0d / GRAVITY); 
t1 = Math.sqrt(2 * height * 1.0d / GRAVITY);


第一次与X轴碰撞后上升至最高点的耗时t2与高度 N%*height会有:

  1. t2 = Math.sqrt((1 - WASTAGE) * 2 * height * 1.0d / GRAVITY); 
t2 = Math.sqrt((1 - WASTAGE) * 2 * height * 1.0d / GRAVITY);


那么总的动画时间为(t1 + t2 + t2),则水平位移速度有(width为X轴总位移):

  1. velocity = width * 1.0d / (t1 + 2 * t2); 
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; 
                double used = (System.currentTimeMillis() - startTime) * 1.0d / 1000;
                x = velocity * used;
                if (0 <= used && used < t1) {
                        y = height - 0.5d * GRAVITY * used * used;
                } else if (t1 <= used && used < (t1 + t2)) {
                        double tmp = t1 + t2 - used;
                        y = (1 - WASTAGE) * height - 0.5d * GRAVITY * tmp * tmp;
                } else if ((t1 + t2) <= used && used < (t1 + 2 * t2)) {
                        double tmp = used - t1 - t2;
                        y = (1 - WASTAGE) * height - 0.5d * GRAVITY * tmp * tmp;
                }

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已被销毁,可以在此处终止动画线程 
surfaceCreated()//通知Surface已被创建,可以在此处启动动画线程
surfaceChanged()//通知Surface已改变
surfaceDestroyed()//通知Surface已被销毁,可以在此处终止动画线程


SurfaceView使用有一个原则,即该界面操作必须在surfaceCreated之后及surfaceDestroyed之前。该回调的监听通过SurfaceHolder设置。代码如下:

  1. //于SurfaceView类中,该类实现SurfaceHolder.Callback接口,如本例中的ParabolaView 
  2. SurfaceHolder holder = getHolder(); 
  3. holder.addCallback(this); 
//于SurfaceView类中,该类实现SurfaceHolder.Callback接口,如本例中的ParabolaView
SurfaceHolder holder = getHolder();
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); 
                                canvas = holder.lockCanvas();
                                … … … … 
                                … … … … 
                                canvas.drawBitmap(bitmap, x, y, paint);
                                holder.unlockCanvasAndPost(canvas);



 

本例中,需要清除屏幕脏区域,出于简便的做法,是将整个SurfaceView背景重复地设置为透明,代码为:

  1. canvas.drawColor(Color.TRANSPARENT, android.graphics.PorterDuff.Mode.CLEAR); 
canvas.drawColor(Color.TRANSPARENT, android.graphics.PorterDuff.Mode.CLEAR);


对于SurfaceView的操作,下面这个链接讲述得更详细,更易理解,推荐去看下:
Android开发之SurfaceView

惯例,Java代码如下,XML请自行实现

本文由Sodino所有,转载请注明出处:http://blog.csdn.net/sodino/article/details/7704084

  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.         } 
ActSurfaceView.java

package lab.sodino.surfaceview;

import lab.sodino.surfaceview.RotateAnimation.InterpolatedTimeListener;
import android.app.Activity;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;

public class ActSurfaceView extends Activity implements OnClickListener, ParabolaView.ParabolaListener, Callback,
                InterpolatedTimeListener {
        public static final int REFRESH_TEXTVIEW = 1;
        private Button btnStartAnimation;
        /** 动画界面。 */
        private ParabolaView parabolaView;
        /** 购物车处显示购物数量的TextView。 */
        private TextView txtNumber;
        /** 购物车中的数量。 */
        private int number;
        private Handler handler;
        /** TextNumber是否允许显示最新的数字。 */
        private boolean enableRefresh;

        public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.main);

                handler = new Handler(this);

                number = 0;

                btnStartAnimation = (Button) findViewById(R.id.btnStartAnim);
                btnStartAnimation.setOnClickListener(this);

                parabolaView = (ParabolaView) findViewById(R.id.surfaceView);
                parabolaView.setParabolaListener(this);

                txtNumber = (TextView) findViewById(R.id.txtNumber);
        }

        public void onClick(View v) {
                if (v == btnStartAnimation) {
                        LogOut.out(this, "isShowMovie:" + parabolaView.isShowMovie());
                        if (parabolaView.isShowMovie() == false) {
                                number++;
                                enableRefresh = true;
                                parabolaView.setIcon(BitmapFactory.decodeResource(getResources(), R.drawable.icon));
                                // 设置起始Y轴高度和终止X轴位移
                                parabolaView.setParams(200, ((ViewGroup) txtNumber.getParent()).getLeft());
                                parabolaView.showMovie();
                        }
                }
        }

        public void onParabolaStart(ParabolaView view) {

        }

        public void onParabolaEnd(ParabolaView view) {
                handler.sendEmptyMessage(REFRESH_TEXTVIEW);
        }

        public boolean handleMessage(Message msg) {
                switch (msg.what) {
                case REFRESH_TEXTVIEW:

                        if (txtNumber.getVisibility() != View.VISIBLE) {
                                txtNumber.setVisibility(View.VISIBLE);
                        }
                        RotateAnimation anim = new RotateAnimation(txtNumber.getWidth() >> 1, txtNumber.getHeight() >> 1,
                                        RotateAnimation.ROTATE_INCREASE);
                        anim.setInterpolatedTimeListener(this);
                        txtNumber.startAnimation(anim);
                        break;
                }
                return false;
        }

        @Override
        public void interpolatedTime(float interpolatedTime) {
                // 监听到翻转进度过半时,更新txtNumber显示内容。
                if (enableRefresh && interpolatedTime > 0.5f) {
                        txtNumber.setText(Integer.toString(number));
                        // Log.d("ANDROID_LAB", "setNumber:" + number);
                        enableRefresh = false;
                }
        }
}


 

  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.         } 
DrawThread.java

package lab.sodino.surfaceview;

import android.view.SurfaceView;

/**
 * @author Sodino E-mail:sodinoopen@hotmail.com
 * @version Time:2012-6-18 上午03:14:31
 */
public class DrawThread extends Thread {
        private SurfaceView surfaceView;
        private boolean running;

        public DrawThread(SurfaceView surfaceView) {
                this.surfaceView = surfaceView;
        }

        public void run() {
                if (surfaceView == null) {
                        return;
                }
                if (surfaceView instanceof ParabolaView) {
                        ((ParabolaView) surfaceView).handleThread();
                }
        }

        public void setRunning(boolean b) {
                running = b;
        }

        public boolean isRunning() {
                return running;
        }
}


 

  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.         } 
ParabolaView.java
package lab.sodino.surfaceview;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
 * @author Sodino E-mail:sodinoopen@hotmail.com
 * @version Time:2012-6-18 上午02:52:33
 */
public class ParabolaView extends SurfaceView implements SurfaceHolder.Callback {
        /** 每30ms刷一帧。 */
        private static final long SLEEP_DURATION = 10l;
        private SurfaceHolder holder;
        /** 动画图标。 */
        private Bitmap bitmap;
        private DrawThread thread;
        private PhysicalTool physicalTool;
        private ParabolaView.ParabolaListener listener;
        /** 默认未创建,相当于Destory。 */
        private boolean surfaceDestoryed = true;

        public ParabolaView(Context context, AttributeSet attrs, int defStyle) {
                super(context, attrs, defStyle);
                init();
        }

        public ParabolaView(Context context, AttributeSet attrs) {
                super(context, attrs);
                init();
        }

        public ParabolaView(Context context) {
                super(context);
                init();
        }

        private void init() {
                holder = getHolder();
                holder.addCallback(this);
                holder.setFormat(PixelFormat.TRANSPARENT);

                setZOrderOnTop(true);
                // setZOrderOnTop(false);

                physicalTool = new PhysicalTool();
        }

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
                surfaceDestoryed = false;
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
                LogOut.out(this, "surfaceDestroyed");
                surfaceDestoryed = true;
                physicalTool.cancel();
        }

        public void handleThread() {
                Canvas canvas = null;

                Paint pTmp = new Paint();
                pTmp.setAntiAlias(true);
                pTmp.setColor(Color.RED);

                Paint paint = new Paint();
                // 设置抗锯齿
                paint.setAntiAlias(true);
                paint.setColor(Color.CYAN);
                physicalTool.start();
                LogOut.out(this, "doing:" + physicalTool.doing());
                if (listener != null) {
                        listener.onParabolaStart(this);
                }
                while (physicalTool.doing()) {
                        try {
                                physicalTool.compute();
                                canvas = holder.lockCanvas();
                                // 设置画布的背景为透明。
                                canvas.drawColor(Color.TRANSPARENT, android.graphics.PorterDuff.Mode.CLEAR);
                                // 绘上新图区域
                                float x = (float) physicalTool.getX();
                                // float y = (float) physicalTool.getY();
                                float y = (float) physicalTool.getMirrorY(getHeight(), bitmap.getHeight());
                                // LogOut.out(this, "x:" + x + " y:" + y);
                                canvas.drawRect(x, y, x + bitmap.getWidth(), y + bitmap.getHeight(), pTmp);
                                canvas.drawBitmap(bitmap, x, y, paint);
                                holder.unlockCanvasAndPost(canvas);
                                Thread.sleep(SLEEP_DURATION);
                        } catch (Exception e) {
                                e.printStackTrace();
                        }
                }
                // 清除屏幕内容
                // 直接按"Home"回桌面,SurfaceView被销毁了,lockCanvas返回为null。
                if (surfaceDestoryed == false) {
                        canvas = holder.lockCanvas();
                        canvas.drawColor(Color.TRANSPARENT, android.graphics.PorterDuff.Mode.CLEAR);
                        holder.unlockCanvasAndPost(canvas);
                }

                thread.setRunning(false);
                if (listener != null) {
                        listener.onParabolaEnd(this);
                }
        }

        public void showMovie() {
                if (thread == null) {
                        thread = new DrawThread(this);
                } else if (thread.getState() == Thread.State.TERMINATED) {
                        thread.setRunning(false);
                        thread = new DrawThread(this);
                }
                LogOut.out(this, "thread.getState:" + thread.getState());
                if (thread.getState() == Thread.State.NEW) {
                        thread.start();
                }
        }

        /** 正在播放动画时,返回true;否则返回false。 */
        public boolean isShowMovie() {
                return physicalTool.doing();
        }

        public void setIcon(Bitmap bit) {
                bitmap = bit;
        }

        public void setParams(int height, int width) {
                physicalTool.setParams(height, width);
        }

        /** 设置抛物线的动画监听器。 */
        public void setParabolaListener(ParabolaView.ParabolaListener listener) {
                this.listener = listener;
        }

        static interface ParabolaListener {
                public void onParabolaStart(ParabolaView view);

                public void onParabolaEnd(ParabolaView view);
        }
}



 

  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.         } 
PhysicalTool.java
package lab.sodino.surfaceview;

/**
 * @author Sodino E-mail:sodinoopen@hotmail.com
 * @version Time:2012-6-18 上午06:07:16
 */
public class PhysicalTool {
        /** 重力加速度值。 */
        private static final float GRAVITY = 400.78033f;
        /** 与X轴碰撞后,重力势能损失掉的百分比。 */
        private static final float WASTAGE = 0.3f;
        /** 起始下降高度。 */
        private int height;
        /** 起始点到终点的X轴位移。 */
        private int width;
        /** 水平位移速度。 */
        private double velocity;
        /** X Y坐标。 */
        private double x, y;
        /** 动画开始时间。 */
        private long startTime;
        /** 首阶段下载的时间。 单位:毫秒。 */
        private double t1;
        /** 第二阶段上升与下载的时间。 单位:毫秒。 */
        private double t2;
        /** 动画正在进行时值为true,反之为false。 */
        private boolean doing;

        public void start() {
                startTime = System.currentTimeMillis();
                doing = true;
        }

        /** 设置起始下落的高度及水平初速度;并以此计算小球下落的第一阶段及第二阶段上升耗时。 */
        public void setParams(int h, int w) {
                height = h;
                width = w;

                t1 = Math.sqrt(2 * height * 1.0d / GRAVITY);
                t2 = Math.sqrt((1 - WASTAGE) * 2 * height * 1.0d / GRAVITY);
                velocity = width * 1.0d / (t1 + 2 * t2);
                LogOut.out(this, "t1=" + t1 + " t2=" + t2);
        }

        /** 根据当前时间计算小球的X/Y坐标。 */
        public void compute() {
                double used = (System.currentTimeMillis() - startTime) * 1.0d / 1000;
                x = velocity * used;
                if (0 <= used && used < t1) {
                        y = height - 0.5d * GRAVITY * used * used;
                } else if (t1 <= used && used < (t1 + t2)) {
                        double tmp = t1 + t2 - used;
                        y = (1 - WASTAGE) * height - 0.5d * GRAVITY * tmp * tmp;
                } else if ((t1 + t2) <= used && used < (t1 + 2 * t2)) {
                        double tmp = used - t1 - t2;
                        y = (1 - WASTAGE) * height - 0.5d * GRAVITY * tmp * tmp;
                } else {
                        LogOut.out(this, "used:" + used + " set doing false");
                        x = velocity * (t1 + 2 * t2);
                        y = 0;
                        doing = false;
                }
        }

        public double getX() {
                return x;
        }

        public double getY() {
                return y;
        }

        /** 反转Y轴正方向。适应手机的真实坐标系。 */
        public double getMirrorY(int parentHeight, int bitHeight) {
                int half = parentHeight >> 1;
                double tmp = half + (half - y);
                tmp -= bitHeight;
                return tmp;
        }

        public boolean doing() {
                return doing;
        }

        public void cancel() {
                doing = false;
        }
}


 

  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.         } 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值