android 2d小游戏用android surfacevieww开发过时了没?

经验141 米
在线时间7 小时
版本JLB22.0
积分 160, 距离下一级还需 40 积分
积分 160, 距离下一级还需 40 积分
机型小米手机2/2S
签到次数10
MIUI版本JLB22.0
本帖最后由 BeyonDroid 于
21:25 编辑
Android游戏框架介绍android的火爆程度在这里就不再赘述了,相信大家都有所了解的。那么,介绍一些android的游戏开发的基础知识。android有很多优秀的控件(如Button、Spinner、Dialog)来提供给大家使用。使用这些控件开发的程序,也能满足绝大多数用户、开发者的需求。但是到了游戏就不同了,android提供给我们的只有两个工具,画布(Canvas)还有画笔(Paint),这么做的原因是因为游戏里的各种形状、功能、颜色、效果的控件太多太多。android要提供所有的是不可能的。所以这就需要我们自己来定义自己的控件,图形,以及逻辑了。本教程分为4个Level
Level0 介绍SurfaceView以及Callback监听那么下面开始进入正题,介绍android游戏开发的框架SurfaceView与Callback。首先来新建一个工程。
1.PNG (71.03 KB, 下载次数: 19)
这里,我们选用android1.6的sdk。工程名称起名为:GameSurfaceViewFrame。然后定义一个类:MySurfaceView,然后实现android.view.SurfaceHolder.Callback接口,代码如下:
public classMySurfaceView extends SurfaceView implements Callback, Runnable {//用于控制SurfaceViewprivateSurfaceHprivateP
publicMySurfaceView(Context context) {super(context);//实例holderholder= this.getHolder();//为SurfaceView添加状态监听holder.addCallback(this);//实例画笔paint= new Paint();//设置画笔颜色为白色paint.setColor(color.white);}
@Overridepublicvoid run() {
@Overridepublicvoid surfaceCreated(SurfaceHolder holder) {Draw();}
@Overridepublicvoid surfaceChanged(SurfaceHolder holder, int format, intwidth,intheight) {}
@Overridepublicvoid surfaceDestroyed(SurfaceHolder holder) {
/*** 自定义绘图函数*/
publicvoid Draw() {Canvascanvas = holder.lockCanvas();canvas.drawText(&MITC&,10, 20, paint);holder.unlockCanvasAndPost(canvas);
在本段代码中,定义了一个SurfaceHolder对象,此对象提供对SurfaceView的大小、格式等控制,并且主要用于监听SurfaceView的状态。其实SurfaceView知识保存当前视图的像素数据,在使用SurfaceView时,不会直接操作SurfaceView,而是通过该Holder对象来实现控制。
通过使用LockCanvas()方法来获得SurfaceView的Canvas对象,同时对Canvas进行锁定,锁定的目的主要是防止在绘图过程中surfaceView被修改。与之对应的是unlockCanvasAndPost(canvas);方法,该方法用于canvas的提交与解锁。
注意:这里的Color使用的是android.graphics.Color。
本类实例的接口Callback需要实现三个方法:当SurfaceView被创建时调用
@Overridepublicvoid surfaceCreated(SurfaceHolder holder) {}当SurfaceView被修改时调用@Overridepublicvoid surfaceChanged(SurfaceHolder holder, int format, intwidth,int height) {}当SurfaceView销毁时调用
@Overridepublicvoid surfaceDestroyed(SurfaceHolder holder) {}最后一步,通过SurfaceHolder的holder.addCallback();方法来实现本对象对SurfaceView的监听。下面我们打开docs找到SurfaceView。
2.png (6.89 KB, 下载次数: 6)
如图,我们可以看到SurfaceView是View的子类。所以,SurfaceView也有View类的触摸屏监听、按键监听等等父类方法。但是,在SurfaceView中,onDraw方法不再执行,而是通过LockCanvas()来实现获取Canvas。所以,我们在这里要自己写一个绘图方法Draw()代码如下:
/*** 自定义绘图函数*/
public void Draw() {Canvas canvas = holder.lockCanvas();canvas.drawText(&MITC&,10, 20, paint);holder.unlockCanvasAndPost(canvas);}
其中代码的功能,第一行用于锁定与获取canvas,最后一行用来解锁画布和提交内容。中间的就是在10,20这个位置通过paint画笔对象来绘制“MITC”字样的文字。接下来,我们还差一步就能够看到该框架的基本执行效果了,让我们继续。我们修改一下MainActivity来让其显示MySurfaceView。废话不说,看代码:
public class MainActivity extends Activity {@Overridepublic voidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//设置全屏this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);requestWindowFeature(Window.FEATURE_NO_TITLE);//显示自定义的SurfaceView视图setContentView(newMySurfaceView(this));}}
至此,框架就可以运行了。看截图:
3.png (3.7 KB, 下载次数: 2)
至此,最简单的Android游戏框架的介绍已经完毕。Level1 实现带有监听的SurfaceView这里,我们实现MITC字样跟随用户手指移动。想要实现这个功能,我们就需要把drawText中已经写死的值修改成变量。//文本的坐标
private int textX = 10, textY= 20;然后修改自定义绘图方法:public void MyDraw() {Canvas canvas = holder.lockCanvas();canvas.drawText(&MITC&,textX, textY, paint);holder.unlockCanvasAndPost(canvas);}最后一步,添加触屏监听。交给大家一个Eclipse下的快捷键,在代码区,按Alt+s,Alt+v 就会弹出复写、实现方法的窗口。
4.png (50.84 KB, 下载次数: 1)
在这里,我们实现onTouchEvent方法代码如下:
@Overridepublicboolean onTouchEvent(MotionEvent event) {textX= (int)event.getX();textY= (int)event.getY();MyDraw();returntrue;}
运行效果图:
5.png (15.34 KB, 下载次数: 3)
显然,这不是大家想要看到的字跟随手指移动,这是由于画布没有刷新,将每次绘制的文本都如假包换的现实了出来。我们所需要做的就是对话不进行“刷新”。Level2 实现刷屏来更新画布上一级留下的问题就是每次都会使用上一次绘图留下的“旧”画布。没有进行更新,相当于我们每次都是在上一次绘图留下的画布上进行新的绘图。所以会出现那种“糟糕”的状况。所以,在使用SurfaceView进行绘图前,应进行刷屏操作。来清空画布上的内容,然后再重新绘制上新的内容。下面,来介绍几种刷屏方式:①
“遮天蔽日大法”之所以给这种方法起这个名字是,是因为这种方式在每次绘制新的图形前,都从新绘制一个等同于屏幕大小的矩形覆盖掉以前的内容。哈哈,起名还算形象吧。废话不说。上代码:
public void MyDraw() {Canvas canvas = holder.lockCanvas();//遮天蔽日大法paint.setColor(Color.BLACK);canvas.drawRect(0, 0, this.getWidth(),this.getHeight(), paint);paint.setColor(Color.WHITE);canvas.drawText(&MITC&, textX,textY, paint);holder.unlockCanvasAndPost(canvas);}
“颜色革命”起这个名字的意思,是因为这次所作的事情相当于把上一种方法进行了一下合并。直接在屏幕上绘制黑色作为背景。上代码:
public void MyDraw() {Canvas canvas = holder.lockCanvas();//颜色革命canvas.drawRGB(0, 0, 0);
canvas.drawText(&MITC&, textX,textY, paint);holder.unlockCanvasAndPost(canvas);}
“新的背景”这种做法的实现方式就是在进行新的绘图之前,绘制一张背景图。这里就不使用代码进行演示了。至此,字体跟随用户手指移动已经全都做完了。但是,该框架已然存在一定的问题;基本上所有游戏(除棋牌类)都不会等用户触屏屏幕或按下某个按键才会更新视图。所以我们下一步就是要做到隔一段时间就自动更新画布。这样,就可以把今后游戏开发中,敌人的AI、场景的变换、玩家生命值等动态信息加入。
下面介绍一个概念“帧频”:在电影、电视以及计算机视频显示器中,帧频(framerate)是指每秒钟放映或显示的帧或图像的数量。在动画和电视中,帧频是由电影与电视工程师学会(SMPTE)制定的标准。24、25和30帧每秒的SMPTE时间代码帧频是通用的,每种用于行业中不同部分。电影的专业帧频是24帧每秒,电视的专业帧频在美国是30帧每秒。——百度百科之所以使用如上的几个帧频是因为人在看动画、电影时,24帧左右就不会有卡顿的感觉。而游戏则不同,我们要把帧频提高到60帧左右,玩家才不会觉得游戏运行存在流畅度问题。(尽管有的性能不太好的手机可能不能达到60帧的帧频,但是我们尽量做到完美的用户体验)好了,帧频介绍完了,我们在下一级就使用多线程来解决自动刷新画布的问题。Level 3 添加多线程在这级里,就要给出最终完成的游戏框架了。所以,我直接上代码,然后再进行进一步解释。
public class MySurfaceView extends SurfaceView implementsCallback, Runnable {// 用于控制SurfaceViewprivate SurfaceHprivate P// 文本的坐标private int textX = 10, textY= 10;// 声明一个线程对象private Thread thread;// 线程标示符
private boolean threadF// 声明画布(之前是放在myDraw里,要体现面向对象的思想,于是拉到外面)private C// 声明Canvas的尺寸private int SCREEN_W,SCREEN_H;
public MySurfaceView(Contextcontext) {super(context);
// 实例holderholder = this.getHolder();// 为SurfaceView添加状态监听holder.addCallback(this);// 实例画笔paint = new Paint();// 设置画笔颜色为白色paint.setColor(Color.WHITE);// 设置焦点setFocusable(true);
@Overridepublic voidsurfaceCreated(SurfaceHolder holder) {// 初始化屏幕尺寸SCREEN_W = this.getWidth();SCREEN_H = this.getHeight();// 在SurfaceView创建时修改线程标识符为真,表名线程启动threadFlag = true;myDraw();}
@Overridepublic voidsurfaceChanged(SurfaceHolder holder, int format, int width,int height) {}
@Overridepublic voidsurfaceDestroyed(SurfaceHolder holder) {threadFlag = false;}
/*** 自定义绘图函数*/public void myDraw() {try {canvas = holder.lockCanvas();// //遮天蔽日大法//paint.setColor(Color.BLACK);//canvas.drawRect(0, 0, this.getWidth(), this.getHeight(), paint);//paint.setColor(Color.WHITE);
// //颜色革命// canvas.drawRGB(0,0, 0);if (canvas !=null) {// 颜色革命刷屏canvas.drawRGB(0,0, 0);canvas.drawText(&MITC&,textX, textY, paint);}} catch (Exception e){/** 这里需要注意,在canvas绘图过程中,很有肯能出现各种各样奇怪的问题。* 所以,在这里使用try catch进行异常捕获。并且在finally语句块中把* canvas解锁。保证下次绘图的正常进行。*/} finally {if (canvas !=null) {holder.unlockCanvasAndPost(canvas);}}}
@Overridepublic booleanonTouchEvent(MotionEvent event) {textX = (int)event.getX();textY = (int)event.getY();myDraw();return true;}
/*** 功能:封装游戏逻辑*/public void logic() {;}
@Overridepublic void run() {while (threadFlag) {longstartTime = System.currentTimeMillis();myDraw();logic();long endTime= System.currentTimeMillis();/** 1000ms /60 = 16.67ms 这里,我们采用15,使帧率限制在最大66.7帧* 如果担心发热、耗电问题,同样可以使用稍大一些的值。经测试80基本为最大值。*/
if (endTime -startTime & 15) {try{Thread.sleep(15- (endTime - startTime));} catch(InterruptedException e) {e.printStackTrace();}}}}}
类对象的封装;着就不多说了,面向对象的好处相信大家都清楚。把类中需要使用的对象设置为私有,保证安全性、提高效率、提高可读性、提高可维护性。。。。。。②
获取屏幕尺寸这里要做的,就是获取屏幕的尺寸。这么做的原因是因为很多游戏的AI逻辑、碰撞检测、边界判断都需要使用到这对值。着这里进行动态获取。③
线程标识符这么做有两个好处:1.避免重复创建线程2.便于销毁线程。先来说第一个。在游戏处理中,通常会写一个死循环让逻辑处理、绘图等等进行循环调用。这样一来,只要不出异常,就会永远循环下去。不会退出,通过这个标识符就可以做到线程销毁的标识,适时的销毁线程。再说第二个。避免重复创建线程。在说之前,先说一下为什么会重复创建线程,android手机上都有Home键和Back键,当用户按下这个按键,手机无论在执行什么程序,都会跳回到桌面。从而导致Surfaceview的状态改变。下面,具体介绍下Home与Back的区别。在用户按下Back键后再重新进入程序,surfaceView的生命周期调用是这样的:surfaceDestroyed→ 构造方法 → surfaceCreated → surfaceChanged。在用户按下Home键后再重新进入程序,surfaceView的生命周期调用是这样的:surfaceDestroyed
→ surfaceCreated → surfaceChanged。区别一目了然,就是构造方法。换句话说,每次按下Back后再返回程序,SurfaceView都会被重新加载。因为这个原因,我们为了避免线程重复创建而出现线程状态异常。最完美的做法就是,线程的初始化和线程的启动都放在试图创建的surfaceCreated中来完成,并且在surfaceDestroyed
中将线程标示符设置为false。这样既可以避免线程已启动异常,同样也可避免线程无线创建的问题。④
控制帧频由于不同手机的性能不同,处理逻辑和绘图的时间也不同,为了尽量使游戏的用户体验一直,我们就要控制帧频,以避免设别性能好游戏就运行的飞快的问题。至于那些设备性能不尽如人意的设别,我们保证他能够不丢帧的运行就可以了(由于本框架比较基础,暂时不考虑跳帧)所以,当设备处理完绘图和逻辑之后,如果处理所用时间小于一帧的时间,那么我们让他休眠够剩下的时间。具体做法代码已经写上去了。⑤
程序的健壮性由于canvas的绘图会出现各种各样奇怪的异常,所以即使他不进行任何的抛出。我们也适用try catch进行一下捕获。以避免程序FC(Force Close)。并且,将解锁画布和提交画布的工作放到finally语句块中,以保证其一定执行,防止影响下一次绘图。
至此,所有的SurfaceView框架就介绍完了。
最后上传附件
/forum.php?mod=attachment&aid=NTEwMjYzfGM1MWIwYTAzNWJmZTMyZjk0MzFlYTJkNDRjNDUyMWI2fDE1MDUzMDY3MjI%3D&request=yes&_f=.pdf
/forum.php?mod=attachment&aid=NTEwMjU4fDViNzE2YzhiMDExNjZmYzQ0MDM3N2U3NDA2YzYwNTMyfDE1MDUzMDY3MjI%3D&request=yes&_f=.zip
分享到微信朋友圈
打开微信,点击底部的“发现”,使用 “扫一扫” 即可将网页分享到我的朋友圈。
已有&2&人评分
perfect!~
太强悍了!
经验31326 米
威望440 米
在线时间1225 小时
版本5.2.11
机型小米手机3 TD版
签到次数204
MIUI版本5.2.11
经验141 米
在线时间7 小时
版本JLB22.0
积分 160, 距离下一级还需 40 积分
积分 160, 距离下一级还需 40 积分
机型小米手机2/2S
签到次数10
MIUI版本JLB22.0
如果有觉着看帖子不爽的同学,直接从最后下载pdf看也成。
经验1843 米
在线时间121 小时
版本4.12.12
MITC负责人
积分 2556, 距离下一级还需 2444 积分
积分 2556, 距离下一级还需 2444 积分
机型小米手机3/4 WCDMA版
签到次数25
MIUI版本4.12.12
MITC官方微博入口:
经验141 米
在线时间7 小时
版本JLB22.0
积分 160, 距离下一级还需 40 积分
积分 160, 距离下一级还需 40 积分
机型小米手机2/2S
签到次数10
MIUI版本JLB22.0
嘿嘿 自己学的一点知识 分享给大家 还有很多不成熟的地方 望大家指正。
经验10945 米
在线时间2059 小时
版本02.056
I am just an ordinary fancier
机型小米手机2/2S
签到次数134
MIUI版本02.056
前来支持e...
经验141 米
在线时间7 小时
版本JLB22.0
积分 160, 距离下一级还需 40 积分
积分 160, 距离下一级还需 40 积分
机型小米手机2/2S
签到次数10
MIUI版本JLB22.0
感谢感谢 嘿嘿 自己平时学的一点知识 还望大家指导
经验1893 米
在线时间187 小时
版本4.7.25
积分 2159, 距离下一级还需 2841 积分
积分 2159, 距离下一级还需 2841 积分
机型小米手机1/1S
签到次数93
MIUI版本4.7.25
看到技术贴,莫名的欣慰啊,前端的帖子会慢慢整出来的,给力~
经验141 米
在线时间7 小时
版本JLB22.0
积分 160, 距离下一级还需 40 积分
积分 160, 距离下一级还需 40 积分
机型小米手机2/2S
签到次数10
MIUI版本JLB22.0
大家一起加油!
经验105 米
在线时间66 小时
版本2.3.7c
积分 147, 距离下一级还需 53 积分
积分 147, 距离下一级还需 53 积分
机型MOTO Defy/Defy+
MIUI版本2.3.7c
MIUI 3000万
MIUI 3000万发烧友纪念勋章
MIUI 2000万
MIUI 2000万发烧友纪念勋章
1000万用户纪念勋章
MIUI1000万用户纪念勋章
小米手机3终身荣誉勋章
小米手机3终身荣誉勋章
小米手机2终身荣誉勋章
小米手机2终身荣誉勋章
MIUI三周年
MIUI三周年纪念勋章
百万壁纸评审纪念勋章
已关注微信
已关注极客秀微信
应用达人勋章
关注腾讯微博
已关注腾讯微博
关注新浪微博
已关注新浪微博
MIUI 100周
100周发布纪念勋章
发烧友俱乐部
发烧友俱乐部
小米手机元器件合体活动勋章
小米求合体勋章
Copyright (C) 2017 MIUI
京ICP备号 | 京公网安备34号 | 京ICP证110507号2012年2月 移动平台大版内专家分月排行榜第二2012年1月 移动平台大版内专家分月排行榜第二2011年5月 移动平台大版内专家分月排行榜第二2011年4月 移动平台大版内专家分月排行榜第二2009年1月 移动平台大版内专家分月排行榜第二
2011年12月 移动平台大版内专家分月排行榜第三2011年11月 移动平台大版内专家分月排行榜第三2011年9月 移动平台大版内专家分月排行榜第三2011年8月 移动平台大版内专家分月排行榜第三2010年3月 移动平台大版内专家分月排行榜第三2010年2月 移动平台大版内专家分月排行榜第三2009年4月 移动平台大版内专家分月排行榜第三2009年3月 硬件/嵌入开发大版内专家分月排行榜第三
2010年12月 移动平台大版内专家分月排行榜第二2010年11月 移动平台大版内专家分月排行榜第二
2011年5月 移动平台大版内专家分月排行榜第三2011年4月 移动平台大版内专家分月排行榜第三2011年3月 移动平台大版内专家分月排行榜第三
2010年12月 移动平台大版内专家分月排行榜第二2010年11月 移动平台大版内专家分月排行榜第二
2011年5月 移动平台大版内专家分月排行榜第三2011年4月 移动平台大版内专家分月排行榜第三2011年3月 移动平台大版内专家分月排行榜第三
2010年12月 移动平台大版内专家分月排行榜第二2010年11月 移动平台大版内专家分月排行榜第二
2011年5月 移动平台大版内专家分月排行榜第三2011年4月 移动平台大版内专家分月排行榜第三2011年3月 移动平台大版内专家分月排行榜第三
本帖子已过去太久远了,不再提供回复功能。& Android游戏开发19:SurfaceView运行机制剖析--处理切换到后台再重新进入程序时的异常
Android游戏开发19:SurfaceView运行机制剖析--处理切换到后台再重新进入程序时的异常
&&&&& &有不少朋友都遇到过这种问题,程序执行时切换到后台,然后再重新进入会报异常,本文就这种问题全面讲解下的运行机制,了解了这些原理你就能自己解决这些问题了。&&&&&& 我们通常会通过单击HOME按键或返回按键等操作切换到后台,之后可能会再次进入程序,这个时候就有可能报异常。这里SurfaceView可能报的异常主要有两点,如下:&&&&&& 一、提交画布异常。如下图(模拟器错误提示,以及 Detail)
public&void&draw()&{ &&
&&&&try&{ &&
&&&&&&&&canvas&=&sfh.lockCanvas(); &&
&&&&&&&&if&(canvas&!=&null)&{ &&
&&&&&&&&&&&&canvas.drawColor(Color.WHITE); &&
&&&&&&&&&&&&canvas.drawBitmap(bmp,&bmp_x,&bmp_y,&paint); &&
&&&&&&&&} &&
&&&&}&catch&(Exception&e)&{ &&
&&&&&&&&Log.v(&Himi&,&&draw&is&Error!&); &&
&&& }&finally&{&&
&&&&&&&&if&(canvas&!=&null)&&
&&&&&&&&&&&&sfh.unlockCanvasAndPost(canvas); &&
}&&&&&&&& 先看备注1这里,之前的文章中我给大家解释过为什么要把 sfh.unlockCanvasAndPost(canvas); 写在finally中,主要是为了保证能正常的提交画布。&&&&&& 今天主要说说备注2,这里一定要判定下是否为空,因为当程序切入后台的时候,canvas是获取不到的!那么canvas一旦为空,提交画布这里就会出现参数异常的错误!&&&&&&&二、线程启动异常。如下图(模拟器错误提示,以及Logcat Detail)&&&&&& 这种异常只是在当你程序运行期间点击Home按钮后再次进入程序的时候报的异常,异常说咱们的线程已经启动!为什么返回按钮就没事?&&&&&& OK,下面我们就要来先详细讲解一下Android中Back和Home按键的机制!然后分析问题,并且解决问题!&&&&&& 先看下面MySurfaceViewAnimation.java的类中的代码:
public&class&MySurfaceViewAnimation&extends&SurfaceView&implements&Callback,&Runnable&{ &&
&&&&private&Thread& &&
&&&&private&SurfaceHolder& &&
&&&&private&Canvas& &&
&&&&private&Paint& &&
&&&&private&Bitmap& &&
&&&&private&int&bmp_x,&bmp_y; &&
&&&&public&MySurfaceViewAnimation(Context&context)&{ &&
&&&&&&&&super(context); &&
&&&&&&&&this.setKeepScreenOn(true); &&
&&&&&&&&bmp&=&BitmapFactory.decodeResource(getResources(),&R.drawable.himi_dream); &&
&&&&&&&&sfh&=&this.getHolder(); &&
&&&&&&&&sfh.addCallback(this); &&
&&&&&&&&paint&=&new&Paint(); &&
&&&&&&&&paint.setAntiAlias(true); &&
&&&&&&&&this.setLongClickable(true); &&
&&&&&&&&th&=&new&Thread(this,&&himi_Thread_one&); &&
&&&&&&&&Log.e(&Himi&,&&MySurfaceViewAnimation&); &&
&&&&public&void&surfaceCreated(SurfaceHolder&holder)&{ &&
&&&&&&&&th.start(); &&
&&&&&&&&Log.e(&Himi&,&&surfaceCreated&); &&
&&&&public&void&surfaceChanged(SurfaceHolder&holder,&int&format,&int&width,&int&height)&{ &&
&&&&&&&&Log.e(&Himi&,&&surfaceChanged&); &&
&&&&public&void&surfaceDestroyed(SurfaceHolder&holder)&{ &&
&&&&&&&&Log.e(&Himi&,&&surfaceDestroyed&); &&
&&&&public&void&draw()&{ &&
&&&&&&&&try&{ &&
&&&&&&&&&&&&canvas&=&sfh.lockCanvas(); &&
&&&&&&&&&&&&if&(canvas&!=&null)&{ &&
&&&&&&&&&&&&&&&&canvas.drawColor(Color.WHITE); &&
&&&&&&&&&&&&&&&&canvas.drawBitmap(bmp,&bmp_x,&bmp_y,&paint); &&
&&&&&&&&&&&&} &&
&&&&&&&&}&catch&(Exception&e)&{ &&
&&&&&&&&&&&&Log.v(&Himi&,&&draw&is&Error!&); &&
&&&&&&&&}&finally&{&&
&&&&&&&&&&&&if&(canvas&!=&null)&&
&&&&&&&&&&&&&&&&sfh.unlockCanvasAndPost(canvas); &&
&&&&&&&&} &&
&&&&public&void&run()&{ &&
&&&&&&&&while&(true)&{ &&
&&&&&&&&&&&&draw(); &&
&&&&&&&&&&&&try&{ &&
&&&&&&&&&&&&&&&&Thread.sleep(100); &&
&&&&&&&&&&&&}&catch&(Exception&ex)&{ &&
&&&&&&&&&&&&} &&
&&&&&&&&} &&
}&&&&&&&& 以上是我们常用的自定义SurfaceView,并且使用Runnable接口老框架了不多说了,其中我在本类的构造、创建、状态改变、消亡函数都加上打印!&&&&&& OK,下面看第一张图:(刚运行程序)&&&&&& 上图的左边部分是Dubug。这里显示我们有一条线程在运行,名字叫&himi_Thread_one&。&&&&&& 上图的右边部分是LogCat日志。大家很清晰的看到,当第一次进入程序的时候,会先进入构造函数、然后是创建view,然后是view状态改变,OK,这个大家都知道!&&&&&& 下面是我来点击Home(手机上的小房子)按键,这时程序处于后台,然后重新进入程序的过程!&&&&&& 上图可以看出我们的线程还是一条,这里主要观察从点击home到再次进入程序的过程,如下所述:&&&&&& 点击home 调用了view销毁,然后进入程序会先进入view创建,最后是view状态改变。&&&&&& 上面的过程很容易理解,重要的角色上场了~Back 按钮!点我点击Back按钮看看发生了什么!&&&&&& 先看左边的Debug一栏,多了一条线程! 看LogCat发现比点击Home按键多调用了一次构造函数!&&&&&& 好了,从我们测试的程序来看,无疑,点击Home 和 点击 Back按钮再次进入程序的时候,步骤是不一样的,线程数量也变了!& &&&& 那么这里就能解释为什么我们点击Back按钮不异常,点击Home会异常了!&&&&&& 原因:因为点击Back按钮再次进入程序的时候先进入的是view构造函数里,那么就是说这里又new了一个线程出来,并启动!那么而我们点击Home却不一样了,因为点击home之后再次进入程序不会进入构造函数,而是直接进入了view创建这个函数,而在view创建这个函数中我们有个启动线程的操作,其实第一次启动程序的线程还在运行,so~这里就一定异常了,说线程已经启动!&&&&&& 有些童鞋会问,我们为何不把th = new Thread(this, &himi_Thread_one&);放在view创建函数中不就好了?!&&&&&& 没错,可以!但是当你反复几次之后你发现你的程序中会多出很多条进程!(如下图)&&&&&& 虽然可以避免出现线程已经启动的异常,很明显这不是我们想要的结果!&&&&&& 那么下面给大家介绍最合适的解决方案:&&&&&& 修改MySurfaceViewAnimation.java:
public&class&MySurfaceViewAnimation&extends&SurfaceView&implements&Callback,&Runnable&{ &&
&&&&private&Thread& &&
&&&&private&SurfaceHolder& &&
&&&&private&Canvas& &&
&&&&private&Paint& &&
&&&&private&Bitmap& &&
&&&&private&int&bmp_x,&bmp_y; &&
&&&&private&boolean&&&&
&&&&public&MySurfaceViewAnimation(Context&context)&{ &&
&&&&&&&&super(context); &&
&&&&&&&&this.setKeepScreenOn(true); &&
&&&&&&&&bmp&=&BitmapFactory.decodeResource(getResources(),&R.drawable.himi_dream); &&
&&&&&&&&sfh&=&this.getHolder(); &&
&&&&&&&&sfh.addCallback(this); &&
&&&&&&&&paint&=&new&Paint(); &&
&&&&&&&&paint.setAntiAlias(true); &&
&&&&&&&&this.setLongClickable(true); &&
&&&&&&&&Log.e(&Himi&,&&MySurfaceViewAnimation&); &&
&&&&public&void&surfaceCreated(SurfaceHolder&holder)&{ &&
&&&&&&&&himi&=&true; &&
&&&&&&&&th&=&new&Thread(this,&&himi_Thread_one&);&&
&&&&&&&&th.start(); &&
&&&&&&&&Log.e(&Himi&,&&surfaceCreated&); &&
&&&&public&void&surfaceChanged(SurfaceHolder&holder,&int&format,&int&width,&int&height)&{ &&
&&&&&&&&Log.e(&Himi&,&&surfaceChanged&); &&
&&&&public&void&surfaceDestroyed(SurfaceHolder&holder)&{ &&
&&&&&&&&himi&=&false;&&
&&&&&&&&Log.e(&Himi&,&&surfaceDestroyed&); &&
&&&&public&void&draw()&{ &&
&&&&&&&&try&{ &&
&&&&&&&&&&&&canvas&=&sfh.lockCanvas(); &&
&&&&&&&&&&&&if&(canvas&!=&null)&{ &&
&&&&&&&&&&&&&&&&canvas.drawColor(Color.WHITE); &&
&&&&&&&&&&&&&&&&canvas.drawBitmap(bmp,&bmp_x,&bmp_y,&paint); &&
&&&&&&&&&&&&} &&
&&&&&&&&}&catch&(Exception&e)&{ &&
&&&&&&&&&&&&Log.v(&Himi&,&&draw&is&Error!&); &&
&&&&&&&&}&finally&{ &&
&&&&&&&&&&&&if&(canvas&!=&null) &&
&&&&&&&&&&&&&&&&sfh.unlockCanvasAndPost(canvas); &&
&&&&&&&&} &&
&&&&public&void&run()&{ &&
&&&&&&&&while&(himi)&{&&
&&&&&&&&&&&&draw(); &&
&&&&&&&&&&&&try&{ &&
&&&&&&&&&&&&&&&&Thread.sleep(100); &&
&&&&&&&&&&&&}&catch&(Exception&ex)&{ &&
&&&&&&&&&&&&} &&
&&&&&&&&} &&
}&&&&&&&& 这里修改的地方有以下几点:&&&&&& 1、我们都知道一个线程启动后,只要run方法执行结束,线程就销毁了,所以我增加了一个布尔值的成员变量 himi(备注1),这里可以控制我们的线程消亡的一个开关!(备注4)&&&&&& 2、在启动线程之前,设置这个布尔值为ture,让线程一直运行。&&&&&& 3、在view销毁时,设置这个布尔值为false,销毁当前线程!(备注3)&&&&&& OK,这里图和解释够详细了,希望大家以后真正开发一款游戏的时候,一定要严谨代码,不要留有后患哈~
本文发布:
本文地址:
发布:鸡啄米
&&( 21:6:0)&&( 22:48:25)&&( 20:58:54)&&( 21:42:35)&&( 18:57:44)&&( 21:47:12)&&( 22:42:8)&&( 22:7:59)&&( 20:34:19)&&( 23:32:33)
平时经常这样操作,有时候会出现问题,看来有些应用需要解决下了
看到Java就头疼!
完全随机文章

我要回帖

更多关于 surfaceview 透明 的文章

 

随机推荐