Android游戏开发中常用的三种视图
2015-09-19 21:30
295 查看
简介
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下载
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories