`
vlinux
  • 浏览: 53281 次
  • 性别: Icon_minigender_1
  • 来自: 火星
社区版块
存档分类
最新评论

Android中的Frame动画

阅读更多

相信有Android手机的人都玩过一款Kuba的游戏(没玩过的我推荐去玩一下),里面用手指接触到屏幕后产生的爆炸效果确实增加了游戏的不少色彩。那么这个是怎么做出来的呢?

 

很明显,这个效果应该是一个动画序列图实现的,即Frame-by-Frame动画。Android实现Frame-by-Frame动画我会的有两种方法:

 

1、animation-list配置,预先将一个动画按照每帧分解成的多个图片所组成的序列。然后再在Android的配置文件中将这些图片配置到动画里面。

 

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item android:drawable="@drawable/explode1" android:duration="50" />
    <item android:drawable="@drawable/explode2" android:duration="50" />
    <item android:drawable="@drawable/explode3" android:duration="50" />
    <item android:drawable="@drawable/explode4" android:duration="50" />
</animation-list>

 

但是由此带来的不便也是显而易见的:drawable目录下拥挤了过多的动画帧文件。如果游戏大起来,动画效果丰富,那么drawable目录下将拥有数量庞大的图片文件,这将是开发人员的灾难(见下图)。


 

2、AnimationDrawable动画。其实我们发现,我们完全可以将同一动画序列的每帧图片都合并到一个大的图片中去,然后读取图片的时候按照约定好的宽、高去读就能准确的将该帧图片精确的读出来了。下图是小雪行走序列图。



 将序列图读出并且转化为动画的核心代码为

animationDrawable = new AnimationDrawable();
Bitmap[] bitmaps = new Bitmap[PlayerConst.PLAYER_XIAOXUE_WALK_FRAME];
for (int frame = 0; frame < bitmaps.length; frame++) {
	Bitmap bitmap = Bitmap.createBitmap(xiaoxueWalkSerBitmap, 
			frame*PlayerConst.PLAYER_XIAOXUE_WALK_WIDTH, 
			lay*PlayerConst.PLAYER_XIAOXUE_WALK_HEIGHT, 
			PlayerConst.PLAYER_XIAOXUE_WALK_WIDTH,
			PlayerConst.PLAYER_XIAOXUE_WALK_HEIGHT);
	animationDrawable.addFrame(new BitmapDrawable(bitmap),100);
}// for,每层有 PLAYER_XIAOXUE_WALK_FRAME 帧
animationDrawable.setOneShot(false);
setBackgroundDrawable(animationDrawable);

 具体例子可以从附件中找到。

 

3、SurfaceView动画。也许你很快就发现,前两个动画都必须依赖View才能展示,并且每个View只能展示一个动画。而在游戏中不可能只有一动画,更恐怖的是很多动画都是随机产生的,并不是事先约定好的,而动态创建/删除View的代价非常高,并不适合做高性能的游戏。这个时候你需要的是SurfaceView。

 

在SurfaceView中的动画有一点是和前边两种动画有区别的:那就是画布上所有的一切都必须自己亲自打理。在前边几个基于Animation的动画你只需关心当前动画的序列即可,其他都由系统帮你处理完毕。而在SurfaceView中,你就是那个处理程序,所有的一切包括背景都必须有你来亲自打理。

 

为此我写了一个框架专门来处理这个琐事,框架只有两个类:AnimationDraw和DrawRunning。其中AnimationDraw则是一个动画类,它负责描述当前动画元素的位置、当前播放到第几帧、每帧的延时是多少、是否重复播放等。

 

import java.util.Date;

import android.graphics.Bitmap;

/**
 * 动画绘画元素
 * @author vlinux
 *
 */
public class AnimationDraw {

	protected float x;
	protected float y;
	protected Bitmap[] bitmaps;
	protected long duration;
	
	protected Long lastBitmapTime;
	protected int step;
	protected boolean repeat;
	
	/**
	 * 动画构造函数-for静态图片
	 * @param x:X坐标<br/>
	 * @param y:Y坐标<br/>
	 * @param bitmap:显示的图片<br/>
	 * @param duration:图片显示的时间<br/>
	 */
	public AnimationDraw(float x, float y, Bitmap bitmap, long duration) {
		Bitmap[] bitmaps = {bitmap};
		this.x = x;
		this.y = y;
		this.bitmaps = bitmaps;
		this.duration = duration;
		this.repeat = true;
		lastBitmapTime = null;
		step = 0;
	}
	
	/**
	 * 动画构造函数
	 * @param x:X坐标<br/>
	 * @param y:Y坐标<br/>
	 * @param bitmap:显示的图片<br/>
	 * @param duration:图片显示的时间<br/>
	 * @param repeat:是否重复动画过程<br/>
	 */
	public AnimationDraw(float x, float y, Bitmap[] bitmaps, long duration, boolean repeat) {
		this.x = x;
		this.y = y;
		this.bitmaps = bitmaps;
		this.duration = duration;
		this.repeat = repeat;
		lastBitmapTime = null;
		step = 0;
	}
	
	
	public Bitmap nextFrame() {

		if (step >= bitmaps.length) {
			// 判断step是否越界
			if( !repeat ) {
				return null;
			} else {
				lastBitmapTime = null;
			}//if
		}// if

		if (null == lastBitmapTime) {
			// 第一次执行
			lastBitmapTime = new Date().getTime();
			return bitmaps[step = 0];
		}// if

		// 第X次执行
		long nowTime = System.currentTimeMillis();
		if (nowTime - lastBitmapTime <= duration) {
			// 如果还在duration的时间段内,则继续返回当前Bitmap
			// 如果duration的值小于0,则表明永远不失效,一般用于背景
			return bitmaps[step];
		}// if
		lastBitmapTime = nowTime;
		return bitmaps[step++];// 返回下一Bitmap
	}
	
	public float getX() {
		return x;
	}

	public float getY() {
		return y;
	}
	
}
 

DrawRunning则是一个负责画图的线程,它是程序的核心。

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.view.SurfaceHolder;

/**
 * 绘画线程
 * 
 * @author vlinux
 * 
 */
public class DrawRunning implements Runnable {

	private List<AnimationDraw> animationDraws;//所有需要画动画的集合
	private List<AnimationDraw> buffers;//缓存前台传入需要展示的动画
	private SurfaceHolder surfaceHolder;
	private boolean running;

	public DrawRunning(SurfaceHolder surfaceHolder) {
		this.surfaceHolder = surfaceHolder;
		animationDraws = new ArrayList<AnimationDraw>();
		buffers = new ArrayList<AnimationDraw>();
		running = true;
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		while (running) {
			synchronized (surfaceHolder) {
				Canvas canvas = null;
				try {
					canvas = surfaceHolder.lockCanvas(null);
					doDraw(canvas);
				} finally {
					if (null != canvas) {
						surfaceHolder.unlockCanvasAndPost(canvas);
					}// if
				}// try
			}// syn
		}// while
	}

	private void doDraw(Canvas canvas) {
		synchronized(this) {
			//检查缓存中是否有需要加入的动画
			if( !buffers.isEmpty() ) {
				animationDraws.addAll(buffers);//加入animationDraws
				buffers.clear();//清空缓存
			}//if
		}//syn
		if( animationDraws.isEmpty() ) {
			return;//如果animationDraws里面是空的那就不用画了
		}//if
		//---这里开始绘画
		Iterator<AnimationDraw> bombIt = animationDraws.iterator();
		while (bombIt.hasNext()) {
			AnimationDraw bomb = bombIt.next();
			Bitmap nextFrame = bomb.nextFrame();
			if (null == nextFrame) {
				//下一Frame为null,说明动画序列已经结束
				//该动画已经完成,从动画集合中删除
				bombIt.remove();
				continue;//while
			}// if
			canvas.drawBitmap(nextFrame, bomb.getX(), bomb.getY(), null);
		}// while
	}

	public void addAnimationDraw(AnimationDraw bomb) {
		synchronized(this) {
			//尽量减少这个的同步响应时间,因为这个方法是前台响应的
			//多0.1秒都会直接反应到用户感知
			buffers.add(bomb);//将需要显示动画的内容加入到缓存
		}//syn
	}

	public void stopDrawing() {
		running = false;
	}

}

 

值得注意的是,我用了一个缓存和两个synchronized来提高前台的响应以及确保对集合类、SurfaceHolder的正确操作。

例子可以见附件。

  • 大小: 8 KB
  • 描述: 小雪行走序列图
  • 大小: 38 KB
分享到:
评论
13 楼 yinhongbiao 2012-10-25  
刚好 要用到这种效果 ,帮助很大啊。 谢谢
12 楼 mingfeng002 2012-03-02  
两个synchronized这样的好处是什么?已经有了canvas = surfaceHolder.lockCanvas(null);这样两次会产生影响吗
11 楼 mylazygirl 2010-12-21  
初学Android,您的这篇文章对我帮助非常大,非常感谢。
10 楼 1986zzrobin 2010-04-19  
"SurfaceView动画。也许你很快就发现,前两个动画都必须依赖View才能展示,并且每个View只能展示一个动画。而在游戏中不可能只有一动画"


View 实现线程类,在局部刷新可以实现的,SurfaceView很多介绍的是双缓冲,不太了解。请博主解释下。
9 楼 稻-草 2010-01-30  
不错,我也是这么想的
8 楼 zhuixinjian 2009-12-31  
vlinux 写道
zhuixinjian 写道
回家仔细研究了下你这段代码。

你每次都是在一个新点画一连串的动画,那么物体移动的动画肯定不行的。

应该是每张图片都带着各自的新坐标来移动。

// 第X次执行  
        long nowTime = System.currentTimeMillis();  
        if (nowTime - lastBitmapTime <= duration) {  
            // 如果还在duration的时间段内,则继续返回当前Bitmap  
            // 如果duration的值小于0,则表明永远不失效,一般用于背景  
            return bitmaps[step];  
        }// if


这段代码也控制不了,是不是永久不消失。事实上小于0的时候,他直接跳出来,返回下一站图片了。

是的,例子代码在控制逻辑上有BUG,这是我当时没考虑清楚。我今天找个时间吧他Fixed掉



呵呵。我对线程编程有点弱,我做了写修改,只是把每张图片都带上新坐标了,实现了物体移动,却不知道怎么能拿到最新的坐标。

以至于每次事件触发,都是从原始起点到终点画了一遍
7 楼 vlinux 2009-12-31  
zhuixinjian 写道
回家仔细研究了下你这段代码。

你每次都是在一个新点画一连串的动画,那么物体移动的动画肯定不行的。

应该是每张图片都带着各自的新坐标来移动。

// 第X次执行  
        long nowTime = System.currentTimeMillis();  
        if (nowTime - lastBitmapTime <= duration) {  
            // 如果还在duration的时间段内,则继续返回当前Bitmap  
            // 如果duration的值小于0,则表明永远不失效,一般用于背景  
            return bitmaps[step];  
        }// if


这段代码也控制不了,是不是永久不消失。事实上小于0的时候,他直接跳出来,返回下一站图片了。

是的,例子代码在控制逻辑上有BUG,这是我当时没考虑清楚。我今天找个时间吧他Fixed掉
6 楼 zhuixinjian 2009-12-30  
回家仔细研究了下你这段代码。

你每次都是在一个新点画一连串的动画,那么物体移动的动画肯定不行的。

应该是每张图片都带着各自的新坐标来移动。

// 第X次执行  
        long nowTime = System.currentTimeMillis();  
        if (nowTime - lastBitmapTime <= duration) {  
            // 如果还在duration的时间段内,则继续返回当前Bitmap  
            // 如果duration的值小于0,则表明永远不失效,一般用于背景  
            return bitmaps[step];  
        }// if


这段代码也控制不了,是不是永久不消失。事实上小于0的时候,他直接跳出来,返回下一站图片了。
5 楼 qdsjj2000 2009-12-17  
佩服啊,高手啊,五体投地啊
4 楼 哇你长得真高 2009-10-20  
好厉害


3 楼 buyajun 2009-10-20  
楼主写的 这几篇 都是 精华中的精华

期待 楼主 更多 佳作 出现
2 楼 healthjava 2009-10-09  
受益匪浅,顶博主。
看楼主的代码里,所有的bitmap都在SurfaceAnimationView构造函数中实例化,这一点似乎不妥。
1 楼 raymondlueng 2009-09-21  
早看到您这文章,我就可以少走很多弯路了,非常感谢!

相关推荐

    Android Frame动画

    在Android开发中,帧动画(Frame Animation)是一种常见的动态效果实现方式,尤其在用户界面设计中,能够增加应用的互动性和吸引力。帧动画是通过连续播放一系列静态图像来创建动态效果,类似于传统的电影制作原理。...

    Android frame 帧动画demo

    在Android开发中,帧动画(Frame Animation)是一种常见的动态效果实现方式,它通过连续播放一系列静态图像来模拟连续动作,类似于传统的电影制作原理。在本示例中,“Android帧动画demo”将展示如何在Android应用中...

    Android Animation Frame逐帧动画

    Frame动画是Android中用于创建序列动画的一种方法,类似于传统的电影制作,通过连续播放多帧静态图片来形成连续的视觉效果。与Tween动画不同,Tween动画是通过对对象属性(如平移、旋转、缩放等)进行平滑过渡来实现...

    Android Animation Frame逐帧动画2

    Frame动画基于一系列连续的静态图像,每张图像是动画中的一个帧,通过快速连续播放这些帧,人眼会将它们融合成动态画面,形成连续的动画效果。在Android中,我们可以使用`AnimationDrawable`类来实现Frame动画。 **...

    Android---Frame动画

    在Android开发中,帧动画(Frame Animation)是一种常见的动画效果,它通过连续播放一系列静态图像来模拟动态效果。本文将深入探讨如何在Android项目中应用帧动画,包括使用`AnimationDrawable`和`ImageView`实现这...

    Android逐帧(Frame)动画

    在Android平台上,动画是应用程序中不可或缺的一部分,可以增强用户体验并为用户提供吸引人的视觉效果。其中,"逐帧动画"(Frame Animation)是一种简单且常用的技术,适用于创建一系列连续的静态图像来模拟动态效果...

    android动画之frame

    在Android开发中,动画是提升用户体验的关键因素之一。帧动画(Frame Animation)是Android提供的一种简单而直观的动画实现方式,适用于实现一系列静态图片按顺序播放的效果,类似于传统的动画胶片。本篇将深入探讨...

    Android 游戏开发之使用AnimationDrable实现Frame动画

    通过阅读“Android 游戏开发之使用AnimationDrable实现Frame动画”相关资料,你可以深入理解`AnimationDrawable`的工作原理,并学习如何将其应用到实际项目中。而`Drawableframe`可能包含了具体的帧动画图片资源,...

    Android 之 Frame逐帧动画

    在本文中,我们将深入探讨Android中的Frame动画,并通过源码分析了解其实现原理。 首先,让我们理解一下什么是Frame动画。在Android中,Frame动画是由`AnimationDrawable`类实现的。这个类是一个可绘制对象,可以...

    Android帧动画和补间动画总结

    在Android开发中,动画是提升用户体验的关键因素,它能让应用变得更加生动有趣。本文将深入探讨两种主要的动画类型:帧动画(Frame Animation)和补间动画(Tween Animation),并提供相应的代码实现示例。 帧动画...

    Android 抖动动画效果

    首先,我们要知道在Android中,动画主要分为两种类型:补间动画(Tween Animation)和帧动画(Frame Animation)。抖动动画属于补间动画的一种,因为它涉及到对象的位置、大小或透明度等属性的变化。补间动画是通过...

    Android中Frame和Tween两类动画笔记源码

    在Android开发中,动画是提升用户体验的关键因素之一。Android提供了两种主要类型的动画:Frame Animation(帧动画)和Tween Animation(补间动画)。本篇将深入探讨这两种动画的原理、使用方式以及源码分析。 **一...

    android气泡动画实现

    在Android开发中,动画是提升用户体验的关键因素之一。"android气泡动画实现"是指在Android应用中创建一种独特的视觉效果,即用户点击屏幕任意位置后,该位置会呈现出气泡爆炸的效果。这种动画通常用于吸引用户的...

    简单的Frame动画

    总结一下,本项目中的"简单的Frame动画"展示了如何使用Android的`AnimationDrawable`来创建一个由6张图片组成的帧动画。在实际开发中,开发者可以根据需求调整图片数量、帧间隔时间,以及如何在代码中控制动画的播放...

    Frame动画

    Frame动画在Android开发中是一种常见的动画形式,它主要用于创建序列帧动画,比如常见的 GIF 图片效果。这种动画机制是通过连续显示一系列预先定义好的图片来实现动态效果的。本篇文章将深入探讨Frame动画的工作原理...

    玩转Android---2D图形及动画---Frame动画

    这篇博文"玩转Android---2D图形及动画---Frame动画"显然会深入探讨如何在Android平台上创建和管理帧动画。帧动画是一种将一系列静态图像连续播放以产生动态效果的技术,常见于游戏和应用程序中的过渡效果。 首先,...

    Android 序列帧动画,开始,结束监听的解决Demo.zip

    在Android开发中,序列帧动画(Frame Animation)是一种常见的动态效果实现方式,它通过连续播放一系列静态图片来创建动画效果,类似于电影胶片的工作原理。本Demo主要关注如何在Android中实现序列帧动画,并添加...

    Android frame by frame animation动画显示

    在Android开发中,帧动画(Frame-by-Frame Animation)是一种常用的技术,用于创建连续的图像序列,模拟视频或gif动图的效果。这种动画通常适用于简单的移动、旋转或渐变效果,比如按钮按下反馈、加载指示器等。接...

    Android 帧(frame)动画

    在Android开发中,帧动画(Frame Animation)是一种常见的动态效果实现方式,它通过连续播放一系列静态图片来模拟动画效果。这种技术尤其适用于那些不需要复杂交互和高性能要求的简单动画场景。接下来,我们将深入...

Global site tag (gtag.js) - Google Analytics