android 多线程实例 动画效果,Android开发教程:SurfaceView使用实例

先上效果图如下:

de58cd3f1a1ea6db1a07a5572d9eb0b9.gif

效果图中,抛物线的动画即是由SurfaceView实现的。

需求:

1.实现抛物线动画

1.1 设计物理模型,能够根据时间变量计算出某个时刻图片的X/Y坐标。

1.2 将图片高频率(相比于UI线程的缓慢而言)刷新到界面中。这儿需要实现将脏界面清屏及刷新操作。

2.文字翻转动画(已解决,见上面的帖子链接)

下面来逐一解决所提出的问题。

-----------------------------------------------------------------------------

分隔线内容与Android无关,请慎读,勿拍砖。谢啦

1.1 设计物理模型,如果大家还记得初中物理时,这并不难。自己写的草稿图见下:

8c38b7e23305610d0a33785babd0e4e7.png

可以有:图片要从高度为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的操作,下面这个链接讲述得更详细,更易理解,推荐去看下:

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

ActSurfaceView.javapackage 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;

}

}

}

DrawThread.javapackage lab.sodino.surfaceview;

import android.view.SurfaceView;

/**

* @author Sodino E-mail:[email protected]

* @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;

}

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值