首页 【贪吃蛇—Java程序员写Android游戏】系列 3. 用J2ME实现Android的Snake Sample详解

【贪吃蛇—Java程序员写Android游戏】系列 3. 用J2ME实现Android的Snake Sample详解

举报
开通vip

【贪吃蛇—Java程序员写Android游戏】系列 3. 用J2ME实现Android的Snake Sample详解 no Pain no Gain no Gavin 博客同步更新至:http://blog.sina.com.cn/deaboway http://blog.csdn.net/deaboway 本次会详细讲解将 Android 的 Snake Sample 移植到 J2ME 上,从而比较二者 的区别和联系。 在《1.Android SDK Sample-Snake详解》中,我们已经详细介绍了Android实现 的Snake项目结构,现在我们要将这个项目用J2ME实现。 一、 J2ME vs....

【贪吃蛇—Java程序员写Android游戏】系列 3. 用J2ME实现Android的Snake Sample详解
no Pain no Gain no Gavin 博客同步更新至:http://blog.sina.com.cn/deaboway http://blog.csdn.net/deaboway 本次会详细讲解将 Android 的 Snake Sample 移植到 J2ME 上,从而比较二者 的区别和联系。 在《1.Android SDK Sample-Snake详解》中,我们已经详细介绍了Android实现 的Snake项目结构,现在我们要将这个项目用J2ME实现。 一、 J2ME vs. Android Android 的 UI 实用、方便,而且很美观,基本无需改动且定制方便。而 J2ME 的高级用户界面比较鸡肋,在现在大多数的应用里都看不到,多数稍微复杂点的界 面都是手工画,或是用一些开源的高级 UI 库。接下来我们简单比较下二者的区 别,为 Snake 项目从 Android 到 J2ME 的移植做准备。 1. 平台 J2ME: 开发平台 Android: 操作系统 2. 工程结构 J2ME: res:资源文件 src:源代码 Android: src:源代码 res\drawable:图片 res\raw:声音 res\values:字符串 assets:数据文件 3. 安装包 J2ME: jad,jar Android: apk 4. 代码结构 J2ME: MIDlet,Canvas,采用继承的方式,只有一个 MIDlet,一般只有一个 Canvas Android: Activity,View,采用继承的方式,只有一个 Activity,一般只有一个 View 5. 入口程序 J2ME: 1 no Pain no Gain no Gavin 博客同步更新至:http://blog.sina.com.cn/deaboway http://blog.csdn.net/deaboway MIDlet 类 Android: Activity 类 6. 主程序结构 J2ME: package com.deaboway.j2me; import javax.microedition.midlet.MIDlet; import javax.microedition.midlet.MIDletStateChangeException; public class MyMidlet extends MIDlet { protected void destroyApp(boolean arg0) throws MIDletStateChangeException { // TODO Auto-generated method stub } protected void pauseApp() { // TODO Auto-generated method stub } protected void startApp() throws MIDletStateChangeException { // TODO Auto-generated method stub } } Android: package com.deaboway.android; import android.app.Activity; import android.os.Bundle; public class myActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(R.layout.main); } } 7. 生命周期-开始 J2ME: startApp(),活动状态,启动时调用,初始化。 Android: onCreate(),返回时也会调用此方法。 onCreate()后调用 onStart(),onStart()后调用 onResume(),此时 Activity 进入 运行状态。 2 no Pain no Gain no Gavin 博客同步更新至:http://blog.sina.com.cn/deaboway http://blog.csdn.net/deaboway 8. 生命周期-暂停 J2ME: pauseApp(),暂停状态,如来电时,调用该接口。 Android: onPause()。 9. 生命周期-销毁 J2ME: destroyApp(),销毁状态,退出时调用。 Android: onStop(),程序不可见时调用 onDestroy(),程序销毁时调用。 10. 刷新 J2ME: 高级 UI 组件由内部刷新实现。低级 UI,canvas 中通过调用线程结合 repaint() 来刷新,让线程不断循环。低级 UI 架构可以用 MVC 方式来实现,建议使用 二级缓存。 Android: 高级 UIHandler 类通过消息的机制刷新。onDraw()刷新接口,低级 UI 开发 者用线程控制更新,在 lockCanvas()和 unlockCanvasAndPost()方法之间绘制。 如果去读 API,我们可以发现 J2ME 中 Canvas 的 repaint()与 Android 中 View 的 invalidate()/postInvalidate()方法实现了相同的功能(连说明文字几乎都一 样…),但是 invalidate()/postInvalidate()两者却有着区别:invalidate() 只能在 UI 这个线程里通过调用 onDraw(Canvas canvas)来 update 屏幕显示,而 postInvalidate()是要在 non-UI 线程里做同样的事情的。这就要求我们做判断, 哪个调用是本 线程的,哪个不是,这在做多线程 callback 的时候尤为重要。 而在 J2ME 中,不管怎样直接调用 repaint()就好了。 11. 绘画 J2ME: Displayable 类。J2me 中所有可显示的组件都是直接或间接的继承了 Displayable,直接的是 Canvas 和 Screen。不同的继承导致了低级 UI 和高级 UI 的区别。J2me 中现成的 UI 组件都是直接或者间接继承了 Screen。只要调 用 Display.getDisplay(MIDLet instan).setCurrrent(Displayable disp),就可以把组 件显示到手机界面上。切换界面的时候也可以使用该接口。 Android: View 类。可见的组件直接或者间接继承了 android.view.View。通过 Activity.setContentView(View view)就可以显示在 android 手机界面上,切换界 面的时候也可以使用该接口。如果是直接继承了 View 而不是 Android 自带的 UI 组件,那么 还要自己去实现它的刷新,类似 J2me 的低级 UI 组件。 12. 画笔 3 no Pain no Gain no Gavin 博客同步更新至:http://blog.sina.com.cn/deaboway http://blog.csdn.net/deaboway J2ME: 高级 UI 组件由内部刷新实现。低级 UI,canvas 中通过调用线程结合 repaint() 来刷新,让线程不断循环。低级 UI 架构可以用 MVC 方式来实现,建议使用 二级缓存。 Android: Canvas 类,Android 绘 制的时候会传入一个参数 Paint。该对象 关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf 示绘制的 风格,比如颜色,字体大小,字体格式等。Android 的 Canvas 不同于 J2ME 的 Canvas,它更像于 J2ME 的 Graphics,用来绘制。 13. 全屏 J2ME: Canvas 中 SetFullScreenMode()。 Android: getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);requestWindowFeature( Window.FEATURE_NO_TITLE); 14. 获得屏幕尺寸 J2ME: Canvas 类的 getHeight()和 getWidth() Android: Display d = getWindowManager().getDefaultDisplay(); screenWidth = d.getWidth(); screenHeight = d.getHeight(); 15. 可绘区域 J2ME: int clipX = g.getClipX(); int clipY = g.getClipY(); int clipWidth = g.getClipWidth(); int clipHeight = g.getClipHeight(); g.clipRect(x, y, width, height); g.setClip(clipX, clipY, clipWidth, clipHeight);//释放当前状态 Android: canvas.save();//保存当前状态 canvas.clipRect(x,y, x+width, y+height) cavnas.resave();//释放当前状态 16. 清屏操作 J2ME: g.setColor(Color.WHITE); g.fillRect(0,0,getWidth(),getHeight()); Android: 4 no Pain no Gain no Gavin 博客同步更新至:http://blog.sina.com.cn/deaboway http://blog.csdn.net/deaboway // 首先定义 paint Paint paint = new Paint(); // 绘制矩形区域-实心矩形 // 设置颜色 paint.setColor(Color.WHITE); // 设置样式-填充 paint.setStyle(Style.FILL); // 绘制一个矩形 canvas.drawRect(new Rect(0, 0, getWidth(), getHeight()), paint); 17. 双缓冲 J2ME: Image bufImage=Image.createImage(bufWidth, bufHeight); Graphics bufGraphics=bufImage.getGraphics(); Android: Bitmap carBuffer = Bitmap.createBitmap(bufWidth, bufHeight, Bitmap.Config.ARGB_4444); Canvas carGp = new Canvas(carBuffer); 18. 图片类 J2ME: Image 类,Image.createImage(path); Android: BitMap 类,BitmapFactory.decodeResource(getResources(),R.drawable.map0); 19. 绘制矩形 J2ME: drawRect 的后两个参数为宽度和高度 Android: drawRect 的后两个参数为结束点的坐标 20. 按键 J2ME: keyPressed() keyRepeated() keyReleased() Android: onKeyDown() onKeyUp() onTracKballEvent() 21. 键值 J2ME: Canvas.LEFT… 5 no Pain no Gain no Gavin 博客同步更新至:http://blog.sina.com.cn/deaboway http://blog.csdn.net/deaboway Android: KeyEvent.KEYCODE_DPAD_LEFT… 22. 触笔 J2ME: pointerPressed(),pointerReleased(),pointerDragged() Android: onTouchEvent() 23. 数据存储 J2ME: Record Management System (RMS) Android: SQLite 数据库,SharedPreferences 类 24. 连接 J2ME: 从 Connector 打开,可以直接在 Connector.Open 时设置连接是否可读写,以 及超时设置 Android: 从 URL 对象打开,必须使用 setDoInput(boolean)和 setDoOutput(boolean)方 法设置,使用 setConnectTimeout(int)不仅可以对连接超时进行设置,还能设 置超时时间,参数为 0 时忽略连接超时 25. 游戏开发包 J2ME: javax.microedition.lcdui.game.*; GameCanvas/Layer/LayerManager/ Sprite/TiledLayer Android: 无专门针对游戏的开发包。 26. 音效 J2ME: Player s=Manager.createPlayer(InputStream); s.prepare();//创建 s.start();//播放 s.stop();//暂停 s.stop();//关闭 s.release();//释放 Android: MediaPlayer 类处理背景音乐 SoundPool 类处理一些简单的音效 6 no Pain no Gain no Gavin 博客同步更新至:http://blog.sina.com.cn/deaboway http://blog.csdn.net/deaboway 27. 显示文本 J2ME: String Android: TextView 类 28. 打印信息 J2ME: System.out.println() Android: Log 类 二、 迁移关键点 1. 基础类和结构 J2ME 程序的主体从 Activity 改变为 MIDlet,TileView 从 View 改变为 Canvas,相关的方法也需要进行调整,但是主体结构和逻辑还是一致的。此 外,有些 J2ME 不支持的类,需要做对应处理。 (1)资源获取,从 xml 改为自行获取: private void initSnakeView() { // 获取图片资源 try { imageRED_STAR = Image.createImage("/redstar.png"); imageRED_STAR = Utils.zoomImage(imageRED_STAR,mTileSize,mTileSize); imageYELLOW_STAR = Image.createImage("/yellowstar.png"); imageYELLOW_STAR = Utils.zoomImage(imageYELLOW_STAR,mTileSize,mTileSize); imageGREEN_STAR = Image.createImage("/greenstar.png"); imageGREEN_STAR = Utils.zoomImage imageGREEN_STAR,mTileSize,mTileSize); ( } catch(Exception e) { Log.info("Create Images: "+e); } // 设置贴片图片数组 resetTiles(4); // 把三种图片存到Bitmap对象数组 loadTile(RED_STAR, imageRED_STAR); loadTile(YELLOW_STAR, imageYELLOW_STAR); loadTile(GREEN_STAR, imageGREEN_STAR); } (2)ArrayList,用 Vector 替换掉: // 坐标数组转整数数组,把Coordinate对象的x y放到一个int数组中——用来保存状 态 private String coordVectorToString(Vector cvec) { 7 no Pain no Gain no Gavin 博客同步更新至:http://blog.sina.com.cn/deaboway http://blog.csdn.net/deaboway int count = cvec.size(); StringBuffer rawArray = new StringBuffer(); for (int index = 0; index < count; index++) { Coordinate c = (Coordinate) cvec.elementAt(index); rawArray.append(c.x+","); rawArray.append(c.y+","); Log.info("coordVectorToString(), c.x="+c.x+",c.y="+c.y); } Log.info("coordVectorToString(), rawArray.toString="+rawArray); return rawArray.toString(); } // 整数数组转坐标数组,把一个int数组中的x y放到Coordinate对象数组中——用来 恢复状态 E 还是用Vector替换ArrayList // @J2M private Vector coordStringToVector(String raw) { Vector coordArrayList = new Vector(); Log.info("coordStringToVector(), raw="+raw); String[] rawArray = Utils.splitUtil(raw,","); Log.info("coordStringToVector(), rawArray.length="+rawArray.length); int coordCount = rawArray.length; for (int index = 0; index < coordCount; index += 2) { Coordinate c = new Coordinate(Integer.parseInt(rawArray[index]), Integer.parseInt(rawArray[index + 1])); coordArrayList.addElement(c); } return coordArrayList; } (3)Bundle,用 RMS 实现: /** *

Title: Snake

*

Copyright: (C) 2011 Gavin's Snake project. Licensed under the Apache License, Version 2.0 (the "License")

* @author Gavin */ package com.deaboway.snake.util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import javax.microedition.rms.RecordStore; public class Bundle extends BaseRMS { private static String[] SECTION = { 8 no Pain no Gain no Gavin 博客同步更新至:http://blog.sina.com.cn/deaboway http://blog.csdn.net/deaboway "\"AL\":","\"DT\":", "\"ND\":","\"MD\":", "\"SC\":","\"ST\":"}; private static int LEN = SECTION.length; private static boolean inited = false; private static Bundle INSTANCE; public static void INIT(){ if(inited)return; inited = true; INSTANCE = new Bundle(); INSTANCE.loadBundles(); } public static Bundle getInstance(){ return INSTANCE; } private String[] CONTENT = new String[LEN]; private Bundle() { super("snake-view"); } public void loadBundles() { try { this.open(); this.close(); } catch (Exception e) { try { this.close(); RecordStore.deleteRecordStore(this.getRMSName()); this.open(); this.close(); } catch (Exception ex) { } } } public void resetBundles() { try { this.close(); RecordStore.deleteRecordStore(this.getRMSName()); this.open(); 9 no Pain no Gain no Gavin 博客同步更新至:http://blog.sina.com.cn/deaboway http://blog.csdn.net/deaboway this.close(); } catch (Exception ex) { } } public void updateBundles() throws Exception { try { this.openonly(); updateData(); if (this.getRecordStore() != null) this.close(); } catch (Exception e) { throw new Exception(this.getRMSName() + "::updateBundles::" + e); } } protected void loadData() throws Exception { try { byte[] record = this.getRecordStore().getRecord(1); DataInputStream istream = new DataInputStream( new ByteArrayInputStream(record, 0, record.length)); String content = istream.readUTF(); int[] start = new int[LEN+1]; for(int i =0;iTitle: Snake

*

Copyright: (C) 2011 Gavin's Snake project. Licensed under the Apache License, Version 2.0 (the "License")

* @author Gavin */ package com.deaboway.snake.util; public class Log{ private static final int FATAL = 0; private static final int ERROR = 1; private static final int WARN = 2; private static final int INFO = 3; private static final int DEBUG = 4; private static int LOG_LEVEL = INFO; public static void info(String string){ if(LOG_LEVEL >= INFO){ System.out.println("[Deaboway][ INFO] " + string); } } public static void debug(String string){ if(LOG_LEVEL >= DEBUG){ System.out.println("[Deaboway][DEBUG] " + string); } } public static void warn(String string){ if(LOG_LEVEL >= WARN){ System.out.println("[Deaboway][ WARN] " + string); } } public static void error(String string){ 12 no Pain no Gain no Gavin 博客同步更新至:http://blog.sina.com.cn/deaboway http://blog.csdn.net/deaboway if(LOG_LEVEL >= ERROR){ System.out.println("[Deaboway][ERROR] " + string); } } public static void fatal(String string){ if(LOG_LEVEL >= FATAL){ System.out.println("[Deaboway][FATAL] " + string); } } } 2. TileView 类 (1)用 Image 替换 BitMap,“private Image[] mTileArray;” (2)private final Paint mPaint = new Paint();不再需要了。直接在 Graphics 中 drawImage 即可 (3)onSizeChanged() 不会被自动调用,需要在构造函数中主动调用,以实现 对应功能。onSizeChanged(this.getWidth(),this.getHeight()); (4)最重要的,用 paint 替换 onDraw,呵呵,Canvas 的核心啊!失去它你伤 不起!!!咱也试试咆哮体!!!!!! 3. SnakeView 类 (1)J2ME 没有 Handler,直接用 Thread 定期 repaint()就 OK。这里要啰嗦几 句。 熟悉 Windows 编程的朋友可能知道 Windows 程序是消息驱动的,并且有全 局的消息循环系统。而 Android 应用程序也是消息驱动的,按道理来说也应 该提供消息循环机制。实际上 Android 中也实现了类似 Windows 的消息循环 机制,它通过 Looper、Handler 来实现消息循环机制,Android 消息循环是针 对线程的(每个线程都可以有自己的消息队列和消息循环)。 Android 系统中 Looper 负责管理线程的消息队列和消息循环。Handler 的作 用是把消息加入特定的(Looper)消息队列中,并分发和处理该消息队列中 的消息。构造 Handler 的时候可以指定一个 Looper 对象,如果不指定则利用 当前线程的 Looper 创建。 一个 Activity 中可以创建多个工作线程或者其他的组件,如果这些线程或者 组件把他们的消息放入 Activity 的主线程消息队列,那么该消息就会在主线程 中处理了。因为主线程一般负责界面的更新操作,并且 Android 系统中的 weget 不是线程安全的,所以这种方式可以很好的实现 Android 界面更新。在 Android 系统中这种方式有着广泛的运用。 如果另外一个线程要把消息放入主线程的消息队列,就需要通过 Handle 对 象,只要 Handler 对象以主线程的 Looper 创建,那么调用 Handler 的 sendMessage 等接口,将会把消息放入队列都将是放入主线程的消息队列。并 且将会在 Handler 主线程中调用该 handler 的 handleMessage 接口来处理消息。 13 no Pain no Gain no Gavin 博客同步更新至:http://blog.sina.com.cn/deaboway http://blog.csdn.net/deaboway 之所以 Android 有这些处理,是因为 Android 平台来说 UI 控件都没有 设计 领导形象设计圆作业设计ao工艺污水处理厂设计附属工程施工组织设计清扫机器人结构设计 成为线程安全类型,所以需要引入一些同步的机制来使其刷新。而对于 J2ME 来说,Thread 比较简单,直接匿名创建重写 run 方法,调用 start 方法执行即 可。或者,也可以从 Runnable 接口继承。实现如下: class RefreshHandler extends Thread { public void run() { while (true) { try { //delay一个延迟时间单位 Thread.sleep(mMoveDelay); } catch (Exception e) { e.printStackTrace(); } // 更新View对象 SnakeView.this.update(); // 强制重绘 SnakeView.this.repaint(); } } }; (2)直接使用 String 代替 TextView 类,在 Canvas 的 paint()中直接绘制各种 提示信息。 (3)在一些地方需要主动调用 repaint()进行强制重绘。 其它具体参考源代码。 4. Snake 类 本类就比较简单了,直接把源代码贴出来如下: /** *

Title: Snake

*

Copyright: (C) 2011 Gavin's Snake project. Licensed under the Apache License, Version 2.0 (the "License")

* @author Gavin */ package com.deaboway.snake; import javax.microedition.lcdui.Display; import javax.microedition.midlet.MIDlet; import javax.microedition.midlet.MIDletStateChangeException; import com.deaboway.snake.util.BaseRMS; import com.deaboway.snake.util.Bundle; import com.deaboway.snake.util.Log; /** * Snake: a simple game that everyone can enjoy. * 14 no Pain no Gain no Gavin 博客同步更新至:http://blog.sina.com.cn/deaboway http://blog.csdn.net/deaboway * 贪吃蛇: 经典游戏,在一个花园中找苹果吃,吃了苹果会变长,速度变快。碰 到自己和墙就挂掉 * */ public class Snake extends MIDlet { public static Display display; public static SnakeView mSnakeView; public static MIDlet SNAKE; public Snake() { Bundle.INIT(); display=Display.getDisplay(this); SNAKE = this; mSnakeView = new SnakeView(); mSnakeView.setTextView(""); mSnakeView.setMode(SnakeView.READY); display.setCurrent(mSnakeView); } protected void destroyApp(boolean arg0) throws MIDletStateChangeException { mSnakeView.saveState(); } protected void pauseApp() { mSnakeView.setMode(SnakeView.PAUSE); } protected void startApp() throws MIDletStateChangeException { // 检查存贮状态以确定是重新开始还是恢复状态 Log.debug("startApp(), BaseRMS.FIRST="+BaseRMS.FIRST); if (BaseRMS.FIRST) { // 存储状态为空,说明刚启动可以切换到准备状态 mSnakeView.setMode(SnakeView.READY); } else { // 已经保存过,那么就去恢复原有状态 // 恢复状态 if (!mSnakeView.restoreState()) { // 恢复状态不成功,设置状态为暂停 mSnakeView.setMode(SnakeView.PAUSE); } } } 15 no Pain no Gain no Gavin 博客同步更新至:http://blog.sina.com.cn/deaboway http://blog.csdn.net/deaboway } 本次就大概介绍这么多,源代码将在下次放出。下次主要讲解源代码的存储 和维护,敬请期待。 更多相关文章,请访问: http://blog.sina.com.cn/deaboway http://blog.csdn.net/deaboway http://www.cnblogs.com/deaboway 以上三个 blog 同步更新。 16
本文档为【【贪吃蛇—Java程序员写Android游戏】系列 3. 用J2ME实现Android的Snake Sample详解】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_750617
暂无简介~
格式:pdf
大小:455KB
软件:PDF阅读器
页数:16
分类:互联网
上传时间:2011-09-07
浏览量:23