Android游戏开发中常用的三种视图

来源:互联网 发布:js 点击按钮刷新div 编辑:程序博客网 时间:2024/06/07 21:49

简介

Android游戏开发中常用的三种视图是View、SurfaceView和GLSurfaceView
下面简单介绍这三种视图:

  • View:显示视图,内置画布,提供图形绘制函数、触屏事件、按键事件函数等;
  • SurfaceView:基于View视图进行拓展的视图类,更适用于2D游戏开发;
  • GLSurfaceView:基于SurfaceView视图类再次拓展的视图类,用于3D游戏开发的视图。

在2D游戏开发中,大致分为两种游戏框架:View游戏框架和SurfaceView游戏框架。两者的主要区别有:

1) 更新画布
View是由系统主UI线程进行更新,通过调用View提供的postInvalidate()和invalidate()这两个函数重新绘制的。棋牌类游戏,其画面更新属于被动更新,这类游戏适用于View游戏框架。

SurfaceView可在UI线程或者新的线程中更新。RPG、飞行射击类游戏,其画面更新属于主动更新,需要不断重绘,这类游戏实用于SurfaceView游戏框架。

2)视图机制
View视图没有双缓冲机制,而SurfaceView视图有。

实例

View游戏框架,此Demo跟踪用户的方向键点击事件,以及触屏事件,具体步骤如下:

1.新建Android项目“ViewDemo”,此处略去若干字

2.创建一个类MyView,此类继承View,代码如下:

package com.cpxiao.viewdemo;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.util.AttributeSet;import android.view.KeyEvent;import android.view.MotionEvent;import android.view.View;/** * Created by cpxiao on 15/9/19. */public class MyView extends View {    private Paint mPaint;    /**     * 重写构造方法     */    public MyView(Context context) {        super(context);        init();    }    public MyView(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    private void init() {        //设置焦点        setFocusable(true);        mPaint = new Paint();        mPaint.setColor(Color.RED);        mPaint.setTextSize(30);    }    /**     * 重写绘图方法     */    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        canvas.drawText("MyView", textX, textY, mPaint);    }    int textX = 100;    int textY = 100;    /**     * 重写按键按下事件的方法     */    @Override    public boolean onKeyDown(int keyCode, KeyEvent event) {        //判断按下的是否为方向键(上下左右)        if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {            textY -= 10;        } else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {            textY += 10;        } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {            textX -= 10;        } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {            textX += 10;        }        //重绘画布的函数有invalidate()和postInvalidate(),区别在于invalidate()不能在子线程中循环调用,而postInvalidate()可以。        invalidate();        //      postInvalidate();        return super.onKeyDown(keyCode, event);    }    /**     * 重写按键抬起事件的方法     */    @Override    public boolean onKeyUp(int keyCode, KeyEvent event) {        return super.onKeyUp(keyCode, event);    }    /**     * 重写触屏事件方法     */    @Override    public boolean onTouchEvent(MotionEvent event) {        int x = (int) event.getX();        int y = (int) event.getY();        if (event.getAction() == MotionEvent.ACTION_DOWN) {            textX = x;            textY = y;        } else if (event.getAction() == MotionEvent.ACTION_MOVE) {            textX = x;            textY = y;        } else if (event.getAction() == MotionEvent.ACTION_UP) {            textX = x;            textY = y;        }        invalidate();        return true;        //      return super.onTouchEvent(event);    }}

3.MainActivity代码如下:

package com.cpxiao.viewdemo;import android.app.Activity;import android.os.Bundle;import android.view.View;import android.view.Window;import android.view.WindowManager;public class MainActivity extends Activity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        View view = new MyView(this);        //隐藏标题栏部分(程序名字)        requestWindowFeature(Window.FEATURE_NO_TITLE);        //隐藏状态栏部分(电池电量、时间等部分)                        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);        setContentView(view);    }}

SurfaceView游戏框架

1.新建Android项目“SurfaceViewDemo”,此处略去若干字

2.新建类“MySurfaceView”,此类继承SurfaceView,并且实现android.view.SurfaceHolder.Callback接口,代码如下:

package com.cpxiao.surfaceviewdemo;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.SurfaceHolder;import android.view.SurfaceView;/** * Created by cpxiao on 15/9/19. */public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {    //SurfaceHolder用于控制SurfaceView的大小、格式等,用于监听SurfaceView的状态。    private SurfaceHolder mSurfaceHolder;    private Paint mPaint;    //初始化文本坐标    private int textX = 100;    private int textY = 100;    public MySurfaceView(Context context) {        super(context);        init();    }    public MySurfaceView(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    private void init() {        //实例SurfaceHolder        mSurfaceHolder = getHolder();        //为SurfaceView添加状态监听        mSurfaceHolder.addCallback(this);        //实例一个画笔        mPaint = new Paint();        mPaint.setColor(Color.RED);        mPaint.setTextSize(30);    }    /**     * 重写SurfaceHolder.Callback接口的三个方法surfaceCreated()、surfaceChanged()、surfaceDestroyed()     */    /**     * 当SurfaceView被创建完成后响应的方法     */    @Override    public void surfaceCreated(SurfaceHolder surfaceHolder) {        myDraw();    }    /**     * 当SurfaceView状态发生改变时响应的方法     */    @Override    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {    }    /**     * 当SurfaceView状态Destroyed时响应的方法     */    @Override    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {    }    /**     * 自定义绘图方法     */    private void myDraw() {        //使用SurfaceHolder.lockCanvas()获取SurfaceView的Canvas对象,并对画布加锁.        Canvas canvas = mSurfaceHolder.lockCanvas();        //得到自定义大小的画布,因为局部绘制,效率更高        //      Canvas canvas = mSurfaceHolder.lockCanvas(new Rect(0,0,200,200));        /**         * 在绘制之前需要将画布清空,否则画布上会显示之前绘制的内容,以下三种方法效果一致*/        canvas.drawRect(0,0,getWidth(),getHeight(),new Paint());        canvas.drawColor(Color.WHITE);        canvas.drawRGB(255, 255, 255);        //通过在Canvas上绘制内容来修改SurfaceView中的数据        canvas.drawText("mySurfaceView", textX, textY, mPaint);        //用于解锁画布和提交        mSurfaceHolder.unlockCanvasAndPost(canvas);    }    /**     * 重写触屏监听方法     */    @Override    public boolean onTouchEvent(MotionEvent event) {        textX = (int) event.getX();        textY  = (int) event.getY();        myDraw();        return true;//      return super.onTouchEvent(event);    }}

3.MainActivity代码如下:

package com.cpxiao.surfaceviewdemo;import android.app.Activity;import android.os.Bundle;import android.view.Window;import android.view.WindowManager;public class MainActivity extends Activity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        requestWindowFeature(Window.FEATURE_NO_TITLE);        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);        MySurfaceView view = new MySurfaceView(this);        setContentView(view);    }}

到此步骤为止,实现了和ViewDemo一致的效果。但是游戏中不会等用户按键或者触屏之后才绘制画布,往往会每隔一段时间刷新画布,比如游戏中的计时器、背景中的流水、动物等等,这些元素虽然不与用户交互但却是动态的。所以游戏开发中需要有一个线程实时更新游戏元素的状态。

完整的MySurfaceView代码如下:

package com.cpxiao.surfaceviewdemo;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.SurfaceHolder;import android.view.SurfaceView;/** * Created by cpxiao on 15/9/19. */public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {    //SurfaceHolder用于控制SurfaceView的大小、格式等,用于监听SurfaceView的状态。    private SurfaceHolder mSurfaceHolder;    private Paint mPaint;    //初始化文本坐标    private int textX = 100;    private int textY = 100;    //声明一个线程    private Thread mThread;    //线程消亡的标志位    private boolean flag = false;    //声明一个画布    private Canvas mCanvas;    //声明屏幕的宽高,获取视图的宽高一定要在视图创建之后才可获取,即surfaceCreated之后获取,否则一直为0    private int screenWidth, screenHeight;    public MySurfaceView(Context context) {        super(context);        init();    }    public MySurfaceView(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    private void init() {        //实例SurfaceHolder        mSurfaceHolder = getHolder();        //为SurfaceView添加状态监听        mSurfaceHolder.addCallback(this);        //实例一个画笔        mPaint = new Paint();        mPaint.setColor(Color.RED);        mPaint.setTextSize(30);        //设置焦点        setFocusable(true);    }    /**     * 重写SurfaceHolder.Callback接口的三个方法surfaceCreated()、surfaceChanged()、surfaceDestroyed()     */    /**     * 当SurfaceView被创建完成后响应的方法     */    @Override    public void surfaceCreated(SurfaceHolder surfaceHolder) {        screenWidth = getWidth();        screenHeight = getHeight();        Log.d("CPXIAO", "screenWidth = " + screenWidth);        Log.d("CPXIAO", "screenHeight = " + screenHeight);        flag = true;        //实例线程        mThread = new Thread(this);        mThread.start();    }    /**     * 当SurfaceView状态发生改变时响应的方法     */    @Override    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {    }    /**     * 当SurfaceView状态Destroyed时响应的方法     */    @Override    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {        flag = false;    }    /**     * 自定义绘图方法     */    private void myDraw() {        try {            //使用SurfaceHolder.lockCanvas()获取SurfaceView的Canvas对象,并对画布加锁.            mCanvas = mSurfaceHolder.lockCanvas();            //得到自定义大小的画布,因为局部绘制,效率更高            //      Canvas canvas = mSurfaceHolder.lockCanvas(new Rect(0,0,200,200));            if (mCanvas != null) {                /**                 * 在绘制之前需要将画布清空,否则画布上会显示之前绘制的内容,以下三种方法效果一致*/                mCanvas.drawRect(0, 0, getWidth(), getHeight(), new Paint());                mCanvas.drawColor(Color.WHITE);                mCanvas.drawRGB(255, 255, 255);                //通过在Canvas上绘制内容来修改SurfaceView中的数据                mCanvas.drawText("mySurfaceView", textX, textY, mPaint);            }        } catch (Exception e) {            e.printStackTrace();        } finally {            if (mSurfaceHolder != null) {                //用于解锁画布和提交                mSurfaceHolder.unlockCanvasAndPost(mCanvas);            }        }    }    /**     * 重写触屏监听方法     */    @Override    public boolean onTouchEvent(MotionEvent event) {        textX = (int) event.getX();        textY = (int) event.getY();        myDraw();        return true;        //      return super.onTouchEvent(event);    }    private int moveX = 10;    private int moveY = 10;    /**     * 程序逻辑代码     */    private void logic() {        if (textX < 0) {            moveX = 10;        } else if (textX > screenWidth) {            moveX = -10;        }        if (textY < 0) {            moveY = 10;        } else if (textY > screenHeight) {            moveY = -10;        }        textX += moveX;        textY += moveY;    }    //设置刷新时间为50毫秒    private static final int REFRESH_TIME = 50;    @Override    public void run() {        while (flag) {            long start = System.currentTimeMillis();            myDraw();            logic();            long end = System.currentTimeMillis();            try {                long use_time = end - start;                if (use_time < REFRESH_TIME) {                    mThread.sleep(REFRESH_TIME - use_time);                }            } catch (Exception e) {                e.printStackTrace();            }        }    }}

代码说明:
1)线程标志位flag

  • 便于消亡线程
  • 防止重复创建线程,避免程序异常

2)获取视图的宽高

  • getWidth()和getHeight()
  • 获取视图的宽高一定要在视图创建之后才可获取,即surfaceCreated之后获取,否则一直为0。因为视图还未创建之前是没有宽高值的

3)绘制时try catch finally

  • 判断Canvas是否为null
  • 为防止绘制出错无法运行到解锁提交这一步,将unlockCanvasAndPost放在finally中执行,执行前先判断一下SurfaceHolder是否为空

4)刷新时间尽可能保持一直,保证帧数

  • 根据逻辑处理用时,计算出每次绘制休眠时间

Demo下载

0 0