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

来源:互联网 发布:历史时间轴软件 编辑:程序博客网 时间:2024/06/05 21:02

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

最后的效果如下面两幅图

这里写图片描述

这里写图片描述

原创粉丝点击