您的位置:首页 > 移动开发 > Android开发

Android 使用SurfaceView进行2D动画的开发

2017-09-05 18:41 579 查看

SurfaceView介绍

SurfaceView是View的子类,可以直接从内存或者DMA等硬件接口取得图像数据,是个非常重要的绘图容器。它的特性是:可以在主线程之外的线程中向屏幕绘图上。这样可以避免画图任务繁重的时候造成主线程阻塞,从而提高了程序的反应速度。在游戏开发中多用到SurfaceView,游戏中的背景、人物、动画等等尽量在画布canvas中画出。

这个视图里内嵌了一个专门用于绘制的Surface,Surface可以这样理解:它是内存中一块区域,它是Surfaceview不可见那个部分,绘图操作作用于它,然后它就会被显卡之类的显示控制器绘制到屏幕上。使用SurfaceView 有一个原则,所有的绘图工作必须得在Surface 被创建之后才能开始。因此我们需要实现SurfaceHolder.Callback接口。通过SurfaceHolder接口可以访问这个surface,getHolder()方法可以得到这个接口。Surface对应了一个内存区,大家都知道,内存区的对象是有生命周期的,可以动态的申请创建和销毁,当然也可能会更新。于是,就有了作用于这个内存区的操作,这些操作就是surfaceCreated/Changed/Destroyed。三个操作放在一起,就是Callback。以上三个生命周期回调方法都有其各自的用途,具体如下:

public void surfaceCreated(SurfaceHolder holder):该方法在SurfaceView被创建时调用,每次创建界面时需要初始化图片、线程等资源,这些代码一般写在surfaceCreated中。

public void surfaceChanged( SurfaceHolder holder , int format , int width , int height ) : 该方法在SurfaceView变化时被调用(如自动横竖屏切换时),在surfaceCreated方法被调用后,该方法至少会被调用一次。当SurfaceView变化时,如果需要改变一些值,这些代码应该放在surfaceChanged方法中。

public void surfaceDestroyed( SurfaceHolder holder ) : 该方法在SurfaceView销毁时被调用。当SurfaceView被销毁时,有些与界面相关的资源应该被释放掉,这些代码应写在surfaceDestroyed方法中。

所以,我们只需继承SurfaceView来开发2D动画效果,并实现SurfaceHolder.Callback接口。

案例演示

下面通过一个2D动画的简单案例来具体演示,动画的场景是:一枚炮弹从屏幕的左下角发射,以抛物线的轨迹划过天空,并在一定的位置爆炸。

MainActivity

package com.surfaceviewdemo;

import android.content.pm.ActivityInfo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;

public class MainActivity extends AppCompatActivity {

//游戏界面
private MySurfaceView gameView ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//设置全屏模式
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
//设置为横屏模式
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
gameView = new MySurfaceView(this);
setContentView(gameView);
}
}


MySurfaceView

package com.surfaceviewdemo;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
* Created by Administrator on 2017/9/5.
*/

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback{

//Activity的引用
Activity activity ;
//画笔引用
Paint paint ;
//绘制线程引用
DrawThread drawThread ;
//背景图片
Bitmap bgBmp ;
//炮弹位图
Bitmap bulletBmp ;
//爆炸位图数组
Bitmap[] explodeBmps ;
//炮弹对象引用
Bullet bullet ;

public MySurfaceView(Activity activity){
super(activity);
this.activity = activity ;
//获取焦点
this.requestFocus();
//设置为可触控
this.setFocusableInTouchMode(true);
//注册回调接口
getHolder().addCallback(this);
}

//重写onDraw方法
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制背景
canvas.drawBitmap(bgBmp,0,0,paint);
//绘制炮弹
bullet.drawSelf(canvas,paint);

}

@Override
public void surfaceCreated(SurfaceHolder holder) {
//创建画笔
paint = new Paint();
//打开抗锯齿
paint.setAntiAlias(true);
//加载图片资源
bulletBmp = BitmapFactory.decodeResource(getResources(),R.drawable.bullet);
bgBmp = BitmapFactory.decodeResource(getResources(),R.drawable.bg);
explodeBmps = new Bitmap[]{
BitmapFactory.decodeResource(getResources(),R.drawable.explode0),
BitmapFactory.decodeResource(getResources(),R.drawable.explode1),
BitmapFactory.decodeResource(getResources(),R.drawable.explode2),
BitmapFactory.decodeResource(getResources(),R.drawable.explode3),
BitmapFactory.decodeResource(getResources(),R.drawable.explode4),
BitmapFactory.decodeResource(getResources(),R.drawable.explode5),
};
//创建炮弹对象
bullet = new Bullet(this, bulletBmp , explodeBmps , 0 , 290 , 1.3f , -5.9f);
//创建绘制线程
drawThread = new DrawThread(this);
//启动绘制线程
drawThread.start();

}

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

}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
//停止绘制线程
drawThread.setFlag(false);
}
}


Bullet炮弹类

package com.surfaceviewdemo;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.transition.Explode;

/**
* Created by Administrator on 2017/9/5.
*/

public class Bullet {
MySurfaceView gameView ;
//位图
private Bitmap bitmap ;
//爆炸动画数组
private Bitmap[] bitmaps ;
//x轴位置
float x ;
//y轴位置
float y ;
//x轴速度
float vx ;
//y轴速度
float vy ;
//生存时间
private float t = 0 ;
//时间间隔
private float timeSpan = 0.5f ;
//炮弹尺寸
int size;
//是否绘制炮弹的标记
boolean explodeFlag = false ;
//爆炸对象引用
Explosion mExplosion ;
public Bullet( MySurfaceView gameView , Bitmap bitmap , Bitmap[] bitmaps, float x , float y , float vx , float vy){
this.gameView = gameView ;
this.bitmap = bitmap;
this.bitmaps = bitmaps ;
this.x = x ;
this.y = y ;
this.vx = vx ;
this.vy = vy ;
size = bitmap.getHeight();
}

/**
* 绘制炮弹的方法
*/
public void drawSelf(Canvas canvas, Paint paint) {
//如果已经爆炸,绘制爆炸动画
if( explodeFlag && mExplosion != null ){
mExplosion.drawSelf(canvas,paint);
}else {
//炮弹前进
go();
//绘制炮弹
canvas.drawBitmap(bitmap,x,y,paint);
}
}

/**
* 炮弹前进的方法
*/
public void go(){
x += vx * t ;
y += vy * t + 0.5f * Constant.G * t * t ;
//特定位置爆炸
if( x >= Constant.EXPLOSION_X || y >= Constant.SCREEN_HEIGHT ){
//创建爆炸对象
mExplosion = new Explosion(gameView,bitmaps,x,y);
//不再绘制炮弹
explodeFlag = true;
return;
}
t += timeSpan ;
}

}


Explosion类

package com.surfaceviewdemo;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;

/**
* Created by Administrator on 2017/9/5.
*/

public class Explosion {
MySurfaceView gameView;
private Bitmap[] bitmaps ;
float x;
float y;

//爆炸动画帧索引
private int anmiIndex = 0 ;
public Explosion( MySurfaceView gameView , Bitmap[] bitmaps , float x , float y){

this.gameView = gameView ;
this.bitmaps = bitmaps ;
this.x = x ;
this.y = y ;
}

//绘制背景的方法
public void drawSelf( Canvas canvas , Paint paint){
//如果动画播放完毕,不再绘制动画效果
if( anmiIndex >= bitmaps.length - 1 ){
return;
}
//绘制数组中某一幅图
canvas.drawBitmap(bitmaps[anmiIndex],x,y,paint);
//当前下标加1
anmiIndex++;
}
}


DrawThread绘制线程

package com.surfaceviewdemo;

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

/**
* Created by Administrator on 2017/9/5.
*/

public class DrawThread extends Thread {
//线程工作标志位
private boolean flag = true ;
//线程休眠时间
private int sleepSpan = 100 ;
//父界面引用
MySurfaceView gameView ;
//surfaceHolder引用
SurfaceHolder surfaceHolder ;
//构造器
public DrawThread(MySurfaceView gameView){
this.gameView = gameView ;
//创建SurfaceHolder对象
this.surfaceHolder = gameView.getHolder();
}

@Override
public void run() {
//声明画布
Canvas c ;
//循环执行刷帧任务
while( flag ){
c = null ;
try {
//锁定画布
c = surfaceHolder.lockCanvas(null);
//锁定surfaceHolder
synchronized ( surfaceHolder ){
//绘制一帧画面
gameView.onDraw(c);
}
}finally {
//释放锁
if( c!= null){
surfaceHolder.unlockCanvasAndPost(c);
}
}
try {
Thread.sleep(sleepSpan);
}catch ( Exception e){
e.printStackTrace();
}
}
}

public void setFlag( boolean flag){
this.flag = flag ;
}
}


Constant常量类

package com.surfaceviewdemo;

/**
* 用于统一管理常量的类
*/
public class Constant {
//屏幕宽度
public static final int SCREEN_WIDTH = 480 ;
//屏幕高度
public static final int SCREEN_HEIGHT = 320 ;
//爆炸x位置
public static final int EXPLOSION_X = 270 ;
//重力加速度
public static final float G = 1.0f ;
}


最后的效果如下面两幅图



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息