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

本文介绍了Android游戏开发中常用的三种视图:View、SurfaceView和GLSurfaceView,重点讲解了View游戏框架和SurfaceView游戏框架的差异。View适用于被动更新的棋牌类游戏,SurfaceView则适合需要主动更新画面的RPG和飞行射击类游戏。通过实例详细展示了如何实现View和SurfaceView的游戏框架。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

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下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值