《从零开始学Android(57页).doc》由会员分享,可在线阅读,更多相关《从零开始学Android(57页).doc(57页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、-从零开始学Android-第 57 页从零开始学安卓经典教程本文是入门android的经典教材,文中既有图片直观的展示,又有文字的详细说明,并且给出了相应代码,对于初学者来说是极好的入门材料。 -谨以感谢原作者什么是OPhoneOPhone是基于Linux、面向移动互联网的终端基础软件及系统解决方案。OPhone SDK是专为OPhone平台设计的软件开发套件,它包括OPhone API,OPhone模拟器,开发工具,示例代码和帮助文档(摘自OPhone官方网站:)。简而言之,OPhone是一个移动终端的操作系统,移动终端包括手机、MID、NetBook等等。与其他领域的编程一样,OPhon
2、e编程并没有什么神秘之处,只需简单的学习就可以掌握大部分的概念。剩下的就是尽情发挥你的想象力了。写作本文的目的为了普及OPhone编程的基本知识,并通过复刻一个坦克大战游戏让读者了解2D游戏编程的简单思路。文中的程序结构和实现方法并非最优,希望能起到一个抛砖引玉的作用,让更多的人加入到OPhone开发的行列中来。谁适合阅读本文虽然本文叫做“从零开始OPhone编程”,但并不能面对那些对编程一无所知的读者。实际上,本文要求读者了解java语言的基本知识,最好会使用eclipse。在文章的每个章节都标有难度,有能力的读者完全可以跳过相对容易的章节直接阅读自己感兴趣的内容。本文的时效性本文只适合当前
3、版本的OPhone SDK(v1.0),本文的代码、图片、链接可能会因时间推移而失效。第一章 搭建开发环境工欲善其技,必先利其器。我们要做的第一件事就是搭建Android开发环境。本文只介绍Windows下的安装方法,Linux下的安装方法请参考官方网站的介绍。与PC编程略有不同的是,Android的程序需要在模拟器中运行。因此,我们需要一个集成开发环境,一个SDK和一个模拟器。因为Android编程使用java语言,所以我们还需要JDK,最好使用安装版本()选用JDK 6 Update 16 Windows版即可。集成开发环境我们选用eclipse,可以使用eclipse3.3到3.5的任意
4、版本()最好下载JDT集成版。然后我们可以从Android官方网站() 下载Android SDK(当然,如果你不能翻墙,可以到国内的网站下载),SDK全部安装完毕之后,还需要安装eclipse插件。插件是用来扩展eclipse功能的。 开发Android用的插件叫ADT(Android Developer Tools),它可以帮助我们完成创建项目,向模拟器部署并运行程序,调试程序等工作。关于ADT的功能,在后面使用中我们会逐渐熟悉。安装ADT的方法如下(以eclipse3.4为例):启动eclipse,选择菜单中的Help - Software Updates点击Add Site点击Arch
5、ive找到OPhone SDK安装目录下toolsophone ADT-0.8.0.zip(因为我已经安装好了ADT,所以出现了重复URL的提示),点击OK即可开始安装ADT安装完毕后还要简单配置一下,打开菜单中的Window - Preferences找到Android项,通过Browse按钮指定Android SDK的安装位置至此为止,Android的安装环境就全部搭建完毕了。下一章节,我们会遇见经典的helloworld,下章见!第二章 创建第一个程序Hello Tank现在开始,我们要真正写作Android程序了。虽然前面安装过程那么复杂,但是写起程序来却是非常简单。而且为了让大家有一
6、个直观的认识,本文不会叙述大段的原理,而是在编码的过程中渗透对原理、概念的讲解。让我们打开eclipse,选择菜单中的File - New - Project选择Android - Android Project下面需要我们输入项目的一些信息,因为我们要复刻经典游戏坦克大战,所以我们的程序就取名Tank这样,一个Android项目就创建完成了,我们可以在eclipse的Package Explorer看到我们的项目托ADT的福,虽然我们只输入了几个名字,但这个项目实际上已经可以运行了。右击项目名,选择Run As - Android Application不出意外的话,你会看到一个手机模拟器被
7、启动,而我们刚刚建立的程序会被运行起来如 果你发现模拟器启动了,而程序并没有被运行,可能需要手工启动程序。这里我们用到一个重要的工具DDMS(Davlik Debug Manager)。运行DDMS快捷方法是点击eclipse右上角的Open Perspective,如果在弹出的列表中没有DDMS,那么点击Others选择DDMS这样我们就打开了DDMS界面,这个工具我们以后会经常用到。刚刚说到模拟器启动了而程序并没有被运行,很可能是在模拟器启动过程中DDMS失去了与模拟器的链接。解决方法很简单:点击Devices标签下的工具栏,选择Reset adb然后右击项目名称,Run As - And
8、roid Application。除了右击运行项目,还可以通过工具栏上的运行按钮启动程序在运行按钮左边的是Debug按钮,这两个我们以后也会经常用到。现 在我们已经有了第一个可以运行的Android,虽然你可能对ADT生成的一堆文件感到一头雾水,也不知道程序界面上那一句“Hello World, Main”是从哪里来的,但是没关系,随着本文的深入你会逐渐熟悉Android项目的目录结构,程序设计的原则和方法,以及调试和部署的方法。现在读者 可以自己熟悉一下模拟器的操作,让我们下章再见。第三章 显示文字和图片从 本章开始,读者就要编写代码了。按照作者的原则少一些理论,多一些实践,代码中可能会有跳
9、跃的地方。但是请大家不要着急,随着学习的深入,你很快就会 了解其中的奥秘。不过在开始之前,我们还是要先来理顺一下思路,看看完成一个坦克大战游戏需要哪些工作:首先,我们需要一个基本的程序,这个程序能够在 Android上运行;这个程序要能够显示图形包括地图,主角和NPC等等;程序能够接受用户的输入,控制主角移动;程序要能够控制NPC和子弹的移动; 程序还能对各种事件做出判断,比如击中敌人,获得物品,胜利或者失败。现在我们就从基本程序开始,一步一步实现它。首先,让我们看一下刚刚生成的文件目录在源文件目录下,只有Main.java和R.java两个文件,刚刚被我们命名成Main.java的文件就是程
10、序的入口文件。而R.java是由插件来维护的资源定义文件,我们先不管它。Main.java内容如下:package org.yexing.android.games.tank;import android.app.Activity;import android.os.Bundle;public class Main extends Activity /* Called when the activity is first created. */Overridepublic void onCreate(Bundle savedInstanceState) super.onCreate(saved
11、InstanceState);setContentView(R.layout.main);很 幸运,Main.java的代码非常之少,而且还有一段注释,以致我们很容易知道函数onCreate的作用,需要解释的只是 setContentView()。先不要管注释中提到的Activity和setContentView的参数R.layout.main,我们使用 setContentView的另一种形式:setContentView(View view)。setContentView的作用是设定当前使用的视图即View(依此理解,可以有很多个View,需要用哪个就可以把他作为 setContentVi
12、ew的参数显示出来)。View是一个非常重要的组件,它可以用来显示文字,图片,也可以接收客户的操作,比如触摸屏,键盘等 等,而我们的游戏中正是需要绘图和交互,看来View很符合我们的需要(但是请注意,使用View并不是我们的最终方案,原因会在后面说明。此处介绍 View是为了讲解基础的图形和用户控制)。下面我们就要订制一个属于自己的View,可以通过继承自系统提供的View,并重载相关的函数来实现。创建类的方法如下:右击包名 New - Class点 击Finish,一个View类就创建好了。这里是第一次创建类,以后就不会有图片演示了,请大家记住的这个方法。GameView创建好了,但是代码还
13、 有一些错误,这里介绍一下eclipse的使用技巧,将鼠标悬停在有错误的位置,或者将光标停在有错误的行,然后按Ctrl+1键,就会出现修改建议,大 部分时候,使用修改建议都可以改正我们的错误,如图可以看出来,刚刚的错误是因为没有创建构造函数,选择修改建议的第二项,增加一个构造函数public GameView(Context context) super(context);/ TODO Auto-generated constructor stub我们的View就创建好了。回到Main.java,刚刚说了,只要将View作为setContentView的参数,这个View就可以被显示出来:pu
14、blic void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState);setContentView(new GameView(this);现在让我们运行模拟器,看看程序变成什么样子了(启动模拟器的方法见第二章)。不要意外,屏幕上就是一片空白,因为我们创建了一个View,但是没有让它显示任何内容。下面我们就会在View上显示一段文字和一张图片。让View显示内容也很简单,只需要重载View的onDraw函数,把相应的语句写入onDraw中即可。打开GameView.java,点击菜单 Source - O
15、verride/Implement Method选中onDraw点击OK下面这段代码就会被加入到程序当中,所有与显示有关的代码都会在这里面完成Overrideprotected void onDraw(Canvas canvas) / TODO Auto-generated method stubsuper.onDraw(canvas);这 里我们遇到了又一个非常重要的类Canvas,Canvas一般翻译成画布,所有的绘图操作都是通过Canvas中的函数来完成的,比如显示文字的函数 Canvas.drawText(),显示位图的函数Canvas.drawBitmap(),以及各种绘制图形的函数
16、如 Canvas.drawRect(),Canvas.drawArc()等等。下面让我们显示一段文字在屏幕上:protected void onDraw(Canvas canvas) / TODO Auto-generated method stubsuper.onDraw(canvas);canvas.drawText(坦克大战, 50, 50, new Paint();坦克大战四个字已经出现在了屏幕上。让我们来详细看一下这条语句:canvas.drawText(坦克大战, 50, 50, new Paint();第 一个参数是要显示的文字,第二、第三个参数是文字在屏幕上的坐标,说到坐标得多
17、讲两句。在2D编程中,屏幕坐标的原点是屏幕的左上角,横向向右增大,纵向 向下增大,如上图所示。最后一个参数是Paint,通常翻译成画笔,它决定了文字或图形的颜色,字体,线条粗细等等,后面用到相应属性的时候会详细介绍。 那么这条语句就是在屏幕上(50,50)的位置用缺省的画笔写出“坦克大战”四个字。另外如果eclipse提示代码错误,不要忘了用Ctrl+1。有了文字,下面就是图像了。显示图像比显示文字略微复杂一些,首先我们要准备一张位图,图片必须是png格式的,文件名只能是小写字母,数字和下划线。然后将这张图片copy到工程的res/drawable目录下。可以直接在eclipse的目录树中粘贴
18、。显示位图的函数是Canvas.drawBitmap(),drawBitmap有很多种形态,我们先看其中最简单的一种canvas.drawBitmap(bitmap, left, top, paint)乍 一看似乎和drawText差不多,4个参数有三个都相同,但这第一个参数bitmap要比文本复杂得多。首先,他是一个Bitmap类实例,因为我们现 在还不需要这个类的其他功能,所以不过多介绍Bitmap,只考虑它是怎么来的。得到Bitmap实例的方法也有很多种,这里只介绍其中的一种BitmapFactory.decodeResource(res, id);此 方法可以返回一个bitmap实例,
19、但是这个函数还需要两个参数res和id。res是Resources实例,而id是一个整数,下面让我们分别了解这 两个参数。res的地位跟bitmap差不多,只需要作为参数被使用,因此,只要得到实例就可以了,获得Resources实例的方法如下:res = context.getResources();天哪,事情越来越复杂了,因为这段代码里面有多了一个陌生面孔context。context是Context实例,Context通常翻译做上下文,这个名称似乎有点晦涩,他究竟是什么呢?让我们回头看看写好的程序public GameView(Context context) super(context)
20、;/ TODO Auto-generated constructor stubpublic void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState);setContentView(new GameView(this);原来,context指向Main类。好了,我们终于找到res的源头了。还有另外一个分支第二个参数id。BitmapFactory.decodeResource(res, id);id是一个整形,它到底是谁的id呢?我们还是得往前面找,还记得我们第一次见到函数setContentView时
21、什么样子么setContentView(R.layout.main);public final class R public static final class attr public static final class drawable public static final int battlecity=0x7f020000;public static final int icon=0x7f020001;public static final class layout public static final int main=0x7f030000;public static final
22、 class string public static final int app_name=0x7f040001;public static final int hello=0x7f040000;果然,位图文件battlecity.png在这里面也被分配了一个id:R.drawable.battlecity,没错,就是它了,这就是我们要找的id。至此为止,我们终于可以使用drawBitmap了。对于一次创建,多次使用的资源,我们把他放到构造函数里面。增加了图形显示的GameView如下:public class GameView extends View Bitmap bmp;public
23、GameView(Context context) super(context);/ TODO Auto-generated constructor stubResources res = context.getResources();bmp = BitmapFactory.decodeResource(res, R.drawable.battlecity);Overrideprotected void onDraw(Canvas canvas) / TODO Auto-generated method stubsuper.onDraw(canvas);canvas.drawText(坦克大战
24、, 0, 50, new Paint();canvas.drawBitmap(bmp, 0, 100, new Paint();运行效果第四章 响应用户事件上一章介绍了如何显示文字和图片,一般来说,下一步就该讲到动画了。可是我们前面说了,使用View不是最终的选择,要实现动画还需要很多复杂的代码。相对来说,学习如何响应用户事件要简单些。本章前半部分讲解按键事件的响应,但是这也不是最终方案,因为实际上的手机可能没有硬键盘,需要使用虚拟键盘,所以后半部分我们会讲解虚拟键盘的设计和实现。同绘图一样,View也是通过回调函数来响应用户事件的。键盘事件的回调函数有多个,以对应不同的事件,我们暂时只用到o
25、nKeyDown,对应按键被按下的事件,其他函数以后用到再介绍。让我们重载onKeyDown(重载一个函数的方法前面章节有介绍):Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) / TODO Auto-generated method stubreturn super.onKeyDown(keyCode, event);onKeyDown有两个参数:keyCode和event,通过keyCode能判断是哪个键被按下,event比较复杂,包含了这次按键更多的信息,我们暂时先不考虑它。现 在我们要通过按键控制主角向四个方向
26、移动。所谓移动,就是将主角的图像在不同的位置显示出来,也就是改变函数drawBitmap中的第二、第三个参数。 比如用户按下右方向键,我们就把横坐标增加,这样下次显示出来的时候,主角就会往右一点。为了节约时间,我们就把刚刚显示的图片BattleCity作为 主角好了。首先定义两个全局变量x和y,然后在onKeyDown中改变x、y的值,然后重绘View。因为代码没有什么难度,所以不做讲解了。public class GameView extends View int x=0, y=0;Overrideprotected void onDraw(Canvas canvas) canvas.dra
27、wBitmap(bmp, x, y, new Paint();Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) / TODO Auto-generated method stubswitch(keyCode) case KeyEvent.KEYCODE_DPAD_UP:y -= 10;break;case KeyEvent.KEYCODE_DPAD_DOWN:y += 10;break;case KeyEvent.KEYCODE_DPAD_LEFT:x -= 10;break;case KeyEvent.KEYCODE_
28、DPAD_RIGHT:x += 10;break;postInvalidate(); /通知系统重绘Viewreturn super.onKeyDown(keyCode, event);完成后我们肯定很想测试一下,但是此时你会发现,按键根本没有任何反应。这就是我们要特殊指出的地方。View被显示时,缺省情况下没有获得焦点,就是说,按键动作没有发送给View,所以需要在构造函数中增加一句public GameView(Context context) setFocusable(true);再运行程序,看看图片是否按照我们的指令运动起来了。前 面说过,很多手机没有硬键盘,所以我们需要一个软键盘的解
29、决方案。软键盘就是在屏幕上显示一个键盘,然后响应用户的触摸屏操作,模拟成键盘操作。对于坦克 大战,我们只需要在屏幕上显示一个模拟的游戏手柄(显示图片的方法大家没有忘记吧,显示位置可以根据模拟器自行调整):在用户触摸模拟手柄上的方向键和开火键时进行相应的操作。我们拿方向键做演示,步骤如下:首先确定四个方向键在屏幕上的区域(上图的红色方框),然后在触摸屏事件的响应函数中判断事件是否发生在方向键区域中,最后如果事件发生在区域中进行相应的操作。下 面,我们引入一个非常有用的类Rect(RectF与Rect基本相同,不过以float作为坐标参数),rect是rectangle的简写,顾名思 义,这个类代
30、表了一个矩形。Rect通过矩形4个边来定义这个矩形的范围。他们分别是left,right,top,bottom,如图所示:转 化为屏幕坐标,top是矩形坐上角的纵坐标,left是矩形坐上角的横坐标,right是矩形右下角的横坐标,buttom是右下角的纵坐标。有了 Rect我们就可以方便的表示虚拟手柄各个键的位置。同时Rect还提供了一些很有用的函数,其中Rect.contains(x, y)能够判断点(x, y)是否在矩形框中,正好是我们需要的。现在我们就可以开始编码了,首先为虚拟键盘的方向键创建Rect(可以用绘图工具测量坐标):Rect rKeyUp = new Rect(56,290,
31、86,320);Rect rKeyDown = new Rect(56, 350, 86, 380);Rect rKeyLeft = new Rect(26, 320, 56, 350);Rect rKeyRight = new Rect(86, 320, 116, 350);然后重载触摸屏响应函数:Overridepublic boolean onTouchEvent(MotionEvent arg0) / TODO Auto-generated method stubreturn super.onTouchEvent(arg0);下面我们要做的是,首先判断触摸屏操作是不是按下,如果是,取得
32、坐标(x,y),然后判断坐标所在的按键,做出相应的操作Overridepublic boolean onTouchEvent(MotionEvent arg0) / TODO Auto-generated method stubif (arg0.getAction() = MotionEvent.ACTION_DOWN) int ax = (int) arg0.getX();int ay = (int) arg0.getY();if (rKeyUp.contains(ax, ay) y -= 10; else if (rKeyDown.contains(ax, ay) y += 10; els
33、e if (rKeyLeft.contains(ax, ay) x -= 10; else if (rKeyRight.contains(ax, ay) x += 10;postInvalidate(); /不要忘记刷新屏幕return super.onTouchEvent(arg0);现在让我们运行一下,每次用鼠标点击模拟手柄的方向键,图片就会移动至此为止,我们介绍了两种响应用户事件的手段,但是要真正完成对一个游戏的控制,还需要更多的工作,后面还有深入的讲解。第五章 小结扫雷游戏的实现目前,我们学习了如何建立Android编程环境,如何显示文字和图片,如何响应用户事件。作为总结,我们要运用这
34、些知识实现一个扫雷游戏。先说游戏规则:扫雷,就是在一个分成若干小格的矩形区域中发现隐藏的地雷,找到它,但是不能触发它。每次翻开一个小格,如果下面是地雷,游戏就失败 了。如果不是地雷,而它的周围8个格中有地雷,那么就会显示周围的地雷数。如果周围8个格中没有地雷,那就是空白的。如果你认为某一格是地雷可以用红旗标 记它,正确标记了所有的地雷或者翻开了所有不是地雷的格就取得胜利。再说用户操作:在Windows中用户可以有三种操作,左键单击,右键单击,左右键同时单击。左键可以翻开一个小格,可以触雷,如果点到了一个空白 格,跟它相联的所有空白格都会被打开。右键可以标记一个格,不会触雷,标记方式有两种,第一
35、次单击用红旗标记,确信此处有雷,再次单击红旗变成问号,表示 可能有雷。左右键同时单击只在此种情况有效,即一个格周围有雷,并且所有的雷都已被用红旗标记出来。此时单击此格会打开周围所有未标记的格,此操作会触 雷,就是说如果标记错了游戏就会失败,所以一定要小心使用。因为在手机上没有右键,所以我们必须设计替代方案,一种方法是设计一个开关图标,点击打开开关 后,所有的操作都被认为是右键和左右键同时单击,另外还可以借助手机上的菜单键,按住菜单键等同打开开关,松开等同关闭开关。为了方便游戏,我们可以同时 实现两个方案。最后让我们分析一下程序的大体思路,从最直观的用户界面开始,我们需要根据游戏的显示区域创建游
36、戏地图,一个m行n列的二维整形数组,数组中的一个 值对应界面上的一个格,不同的数值可以表示不同的状态,比如0表示空白格,1表示此格周围有一个雷,2表示有两个雷等等。因为雷的位置是随机的,所以这张 地图需要在每次游戏开始的时候被初始化。另外因为这张地图不能直接显示给用户看,所以我们还需要另一个同样大小的地图把它盖起来。并且也用不同的数值来表 示一个格是否被翻开,或者被标记等等。这样每次刷新屏幕我们会根据地图上的数值在屏幕上对应的位置显示不同的图片,就是我们看到的游戏界面。这种使用多层 地图的技术在游戏中非常常见。这两个地图可以在同一个二维数组中,也可以分开两个数组,为了便于理解,我们使用两个数组
37、分别表示。另外,界面上还要显示当 前剩余的地雷数量和游戏时间。剩余地雷数是地雷总数减去用户标记的地雷数,可以是负值。游戏时间是每次新游戏开始时启动的一个计时器,我们知道,如果要时 间连贯显示,就必须不停的自动刷新屏幕,但是回顾前面四章的内容,并没有讲解如何循环刷新屏幕,所以在这里会介绍一种使用Handler刷新屏幕的方法, 这种方法比较简单,但是效率并不高,所以后面我们还会介绍另外一种更有效的方法。接着来看用户事件的响应,虽然用户也可以用键盘来操作,但那样就失去了扫雷游戏追求速度的快感,所以我们只设计使用触摸屏的方案。当用户点击屏幕 时,首先判断点击在哪一格,再根据用户点击的方法,以及被点击格
38、的状态,判断用户操作的结果,改变地图上相应格的数值,刷新后用户操作的结果就会在屏幕上 反映出来。另外我们还需要一些游戏状态的标志,比如游戏胜利,失败或是正在游戏中。如果游戏胜利,还需要存储记录,由于存储操作前面没有讲到,我们暂时放弃这个功能。为了缩减篇幅,下面使用源代码直接讲解,源程序的eclipse工程文件已经随本文一起提供下载,这里就算是一个源程序导读(在源程序的res/raw目录下有一首mp3很好听哦)。首先看Main.javaOverrideprotected void onPause() * 在程序被挂起或者退出的时候改变游戏状态以结束游戏循环gameView.gameState =
39、 GameView.STATE_LOST;super.onPause();这里我们对onPause做一个说明,也是对Android程序生命周期的一个简单介绍。详细内容会随着文章的深入慢慢讲解。大家知道在pc中,多 个程序是可以同时运行的,即多进程。在手机中,程序依然可以多进程运行,但是还有一些不同:首先是屏幕,当一个应用启动后,他要独占屏幕。这样一些有打断 功能程序运行起来后,当前的程序就不可见了,最简单的例子就是来电。当你正在游戏,突然有电话打进来,来电程序会占领屏幕,你的游戏转到后台运行(这时 onPause事件就会被触发);另一个不同的地方是,如果一个程序被转到后台太长时间而没有再次被激
40、活,那么系统会结束这个程序。在结束之前会触发相应 的事件(onSaveInstanceState,后面会介绍)。而与之对应的,当程序开始,触发onCreate事件时,我们需要检测当前程序是第一次 运行还是被在后台销毁后重新运行,如果是重新运行,我们需要装载程序销毁前的状态信息。这里我们用onPause的方法并不正确,因为程序挂起的时候不应该让游戏结束,但是为了简化代码,我们暂时先这样做,后面会加以改进。程序的主要内容在GameView.java中,随本章提供的源代码中有详细的注释,请大家自行阅读源码。第六章 SurfaceView动画前 面介绍的内容,还是比较简单的,应用这些知识,可以完成一些
41、非实时游戏,比如井字棋等,或者一些画面刷新不是很频繁、实时性不强的游戏,比如我们前面做的 扫雷。但是我们的目标是坦克大战,对操作的实时性要求比较高,更有很多的NPC需要处理,绘图的工作量也很大,所以我们要用一个新的视图类 SurfaceView代替View来完成显示工作。SurfaceView与View有一些不同,但是我们只用其中的一个特性:在主线程之外的线程中向 屏幕上绘图。这样就可以避免在画图任务繁重的时候造成主线程阻塞,从而提高程序的反应速度。首先让我们重新定义一个GameView 类,让他继承自SurfaceView,并且要实现SurfaceHolder.Callback接口。为什么要
42、实现Callback接口呢?因为使用 SurfaceView有一个原则,所有的绘图工作必须得在Surface被创建之后才能开始(Surface表面,这个概念在图形编程中常常被提到。 基本上我们可以把它当作显存的一个映射,写入到Surface的内容可以被直接复制到显存从而显示出来,这使得显示速度会非常快),而在Surface被 销毁之前必须结束。所以Callback中的surfaceCreated和surfaceDestroyed就成了绘图处理代码的边界。我们直接让 GameView类实现Callback接口,使程序更简洁一些。GameView被创建,并补充了构造函数之后就是这个样子(创建类和添
43、加构造函数的方法前面有介绍哦)package org.yexing.android.games.tank;import android.content.Context;import android.view.SurfaceHolder;import android.view.SurfaceView;import android.view.SurfaceHolder.Callback;public class GameView extends SurfaceView implements Callback public GameView(Context context) super(contex
44、t);/ TODO Auto-generated constructor stubpublic void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) / TODO Auto-generated method stubpublic void surfaceCreated(SurfaceHolder arg0) / TODO Auto-generated method stubpublic void surfaceDestroyed(SurfaceHolder arg0) / TODO Auto-generate
45、d method stub这 里我们有看到了一个新的类SurfaceHolder,我们权且把它当作一个Surface的控制器,用它来操作Surface。因为我们现在还不需要直 接操作Surface,所以我们不做深入讲解。而唯一要使用的是SurfaceHolder.addCallback,即为SurfaceHolder添 加回调函数。原因前面我已经说明了,方法如下:public GameView(Context context) super(context);/ TODO Auto-generated constructor stubgetHolder().addCallback(this);现
46、在我们可以运行一下,跟第一次使用View一样,界面上什么也没有。因为我们还没有编写绘图的代码嘛。前面说过,我们之所以使用SurfaceView代替View,是因为SurfaceView可以在主线程之外的线程中进行绘图操作,从而提高界面的反应速度。下面我们要做的就是创建一个用来绘图的线程。不过在这之前我们可以先了解一些关于游戏循环的知识:我 们知道,一般的应用程序是用户驱动的,就是用户操作了,程序再来响应。而我们的游戏呢,不管用户有没有操作,都会有一些变化,最明显的就是npc会移动、 发生世界事件等。因此,我们可以说,游戏程序在一个无限循环当中,我们就把它叫做游戏循环。那么在游戏循环中要做哪些工
47、作呢?让我们用一个流程图来说明游 戏循环的过程:这只是我们假设的流程,不同的游戏肯定会都有些变化。而且细节上会有更多的差别。了解了游戏循环,下面的工作就是建立一个线程,线程中包含一个游戏循环,在游戏循环中更新游戏的各种数据,并根据这些数据将游戏画面绘制在Surface上最终显示给玩家。创 建线程的方法很简单,我们不需要知道Thread的很多高级特性。只需要知道,在线程中完成具体的工作需要重载run()函数。线程通过start()函 数启动。然后就会执行run()函数中的内容,run()函数执行结束后线程就会终止。因此我们将游戏循环放在run()函数中。通过start()启动 循环,并通过适当的方式结束循环进而结束整个线程。还要注意一点,所有对Surface的操作都必须要保证同步,因此我们会使用Synchronized 关键字,同步SurfaceHolder。增加了GameThread后的代码如下:public class GameView extends SurfaceView implements Callback