JavaME 开发体会:线程编程解决手机地图按住方向键不放地图移动问题
初从 JavaEE 开发转入 JavaME 开发,线程编程技术越发显得重要.作者以一个真实项目中的体会,演示了线程编程在 JavaME 开发中的使用,可以巧妙地解决一些看似疑难的问题.
背景:以下代码是作者写的一个 JavaMe 手机地图移动的代码.
ImageMIDlet.java 源码如下:
/** * 文件名:ImageMIDlet.java * * 版本信息: * 日期:2009-10-17 * Copyright XXX Corporation 2009 * 版权所有 * */ package image.downed.move; import javax.microedition.lcdui.Command; import javax.microedition.lcdui.CommandListener; import javax.microedition.lcdui.Display; import javax.microedition.lcdui.Displayable; import javax.microedition.midlet.MIDlet; import javax.microedition.midlet.MIDletStateChangeException; /** * * 项目名称:Imagedownedmove * 类名称:ImageMIDlet * 类描述:This is the main class of the image downed move. * 创建人:Defonds * 创建时间:2009-10-17 上午11:02:17 * 修改人:Defonds * 修改时间:2009-10-17 上午11:02:17 * 修改备注: * @version * */ public class ImageMIDlet extends MIDlet implements CommandListener { /** * The canvas is the region of the screen. */ ImageCanvas myCanvas; /** * The command objects appears as buttons. */ private Command exitCommand = new Command("退出",Command.EXIT,99); /** * Initialize the canvas and the commands. */ public ImageMIDlet() { myCanvas = new ImageCanvas(Display.getDisplay(this)); myCanvas.addCommand(exitCommand); myCanvas.setCommandListener(this); } //------------------------------------- //Implimentation of MIDlet. /** * If the MIDlet was usring resources,it should release them in this method. */ /* (non-Javadoc) * @see javax.microedition.midlet.MIDlet#destroyApp(boolean) */ protected void destroyApp(boolean arg0) throws MIDletStateChangeException {} /** * This method is called to notify the MIDlet to enter a paused state. * The MIDlet should use this opportunity to release shared resources. */ /* (non-Javadoc) * @see javax.microedition.midlet.MIDlet#pauseApp() */ protected void pauseApp() { } /** * Start the application. */ /* (non-Javadoc) * @see javax.microedition.midlet.MIDlet#startApp() */ protected void startApp() throws MIDletStateChangeException { myCanvas.start(); } //--------------------------------- // implimentation of CommandListener. /** * Respond to a command issued on the Canvas. * (either reset or exit). */ /* (non-Javadoc) * @see javax.microedition.lcdui.CommandListener#commandAction(javax.microedition.lcdui.Command, javax.microedition.lcdui.Displayable) */ public void commandAction(Command arg0, Displayable arg1) { if(arg0 == exitCommand) { try { destroyApp(false); notifyDestroyed(); } catch (MIDletStateChangeException e) {} } } }
ImageCanvas.java 源代码如下:
/** * 文件名:ImageCanvas.java * * 版本信息: * 日期:2009-10-17 * Copyright XXX Corporation 2009 * 版权所有 * */ package image.downed.move; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import javax.microedition.io.Connector; import javax.microedition.io.HttpConnection; import javax.microedition.lcdui.Canvas; import javax.microedition.lcdui.Display; import javax.microedition.lcdui.Graphics; import javax.microedition.lcdui.Image; /** * * 项目名称:Imagedownedmove * 类名称:ImageCanvas * 类描述:This class represents the region of the screen. * 实现按住向下键不放,N 张图片一起下移的算法. * 创建人:Defonds * 创建时间:2009-10-17 上午11:32:14 * 修改人:Defonds * 修改时间:2009-10-17 上午11:32:14 * 修改备注: * @version * */ public class ImageCanvas extends Canvas { //------------------------------- //fields /** * A handle to the screen of the device. */ Display myDisplay; /** * 每按一下向下键,图片向上移动的距离 */ private final int distance = 5; /** * 每按一下向下键,还有多少距离显示空白 */ private int ydown = 0; /** * 手机屏幕中心点坐标 */ private final int x = getWidth()/2,y = getHeight()/2; /** * 下载图片的宽度 */ private final int imageWidth = 300; /** * 判断是否显示第5,6张,停止显示第1,2张 */ boolean showNew = false; /** * 6个图片的初始坐标 */ private int x1 = x - imageWidth,y1 = y - imageWidth; private int x2 = x,y2 = y - imageWidth; private int x3 = x - imageWidth,y3 = y; private int x4 = x,y4 = y; private int x5 = x - imageWidth,y5 = getHeight() - 1; private int x6 = x,y6 = getHeight() - 1; /** * 要画的图片 */ Image image1,image2,image3,image4,image5,image6; /** * 要下载的图片的URL(规格:全部是 300 X 300) */ String url1 ="http://img1.mapbar.com/maplite/mapbank/baidu/8/116_39/4_7.png"; String url2 ="http://img3.mapbar.com/maplite/mapbank/baidu/8/116_39/5_7.png"; String url3 ="http://img.mapbar.com/maplite/mapbank/baidu/8/116_39/6_6.png"; String url4 ="http://mappng.baidu.com/maplite/mapbank/baidu/5/11_4/3_3.png"; String url5 = "http://img3.mapbar.com/maplite/mapbank/baidu/8/109_42/1_1.png"; String url6 = "http://www.sunyn.com/kmtour/UploadFiles_7357/200706/20070625215831613.png"; /** * 要下载图片的字节流 */ byte[] byteofimg1,byteofimg2,byteofimg3,byteofimg4,byteofimg5,byteofimg6; //------------------------- //Initialization /** * Constructor merely sets the display. */ public ImageCanvas(Display d) { super(); myDisplay = d; ydown = imageWidth - getHeight()/2; try { byteofimg1 = loadHttpFile(url1); System.out.println("byteofimg1.length="+byteofimg1.length); byteofimg2 = loadHttpFile(url2); System.out.println("byteofimg2.length="+byteofimg2.length); byteofimg3 = loadHttpFile(url3); System.out.println("byteofimg3.length="+byteofimg3.length); byteofimg4 = loadHttpFile(url4); System.out.println("byteofimg4.length="+byteofimg4.length); } catch (IOException e) { e.printStackTrace(); } } /** * This is called as soon as the application begins. */ void start() { myDisplay.setCurrent(this); repaint(); } /** * 清屏 */ public void clearScreen(Graphics g) { //g.setColor(0, 0, 0);//要设置的底色-黑色 g.setColor(255,255,255); //要设置的底色-白色 g.fillRect(0, 0, this.getWidth(), this.getHeight()); } /** * 键盘事件的重写 */ protected void keyPressed(int keyCode) { ydown = ydown - distance; //如果出现空白,停止显示第1,2张图片,开始显示3,4以及5,6 if(ydown < 0) { try { byteofimg5 = loadHttpFile(url5); } catch (IOException e) { e.printStackTrace(); } System.out.println("byteofimg5.length="+byteofimg5.length); try { byteofimg6 = loadHttpFile(url6); } catch (IOException e) { e.printStackTrace(); } System.out.println("byteofimg6.length="+byteofimg6.length); showNew = !showNew; //ydown重置 ydown = imageWidth; System.out.println("byteofimg2.length="+byteofimg2.length); } int action = getGameAction(keyCode); switch(action) { case Canvas.DOWN: y1 = y1 - distance; y2 = y2 - distance; y3 = y3 - distance; y4 = y4 - distance; if(showNew) { y5 = y5 - distance; y6 = y6 - distance; } repaint(); } } /** * 下载图片函数 */ public byte[] loadHttpFile(String url) throws IOException { byte[] byteBuffer; HttpConnection hc = (HttpConnection) Connector.open(url); System.out.println("loadHttpFile:"+url); try { hc.setRequestMethod(HttpConnection.GET); InputStream is = hc.openInputStream(); try { int len = (int) hc.getLength(); if (len > 0) { byteBuffer = new byte[len]; int done = 0; while (done < len) { done += is.read(byteBuffer, done, len - done); } } else { ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] buffer = new byte[512]; int count; while ( (count = is.read(buffer)) >= 0 ) { bos.write(buffer, 0, count); } byteBuffer = bos.toByteArray(); } } finally { is.close(); } } finally { hc.close(); } return byteBuffer; } //------------------------------------------- //Graphics methods /** * Clear the screen and display the image. */ /* (non-Javadoc) * @see javax.microedition.lcdui.Canvas#paint(javax.microedition.lcdui.Graphics) */ protected void paint(Graphics arg0) { clearScreen(arg0); if(!showNew) { image1 = Image.createImage(byteofimg1,0, byteofimg1.length); image2 = Image.createImage(byteofimg2,0, byteofimg2.length); image3 = Image.createImage(byteofimg3,0, byteofimg3.length); image4 = Image.createImage(byteofimg4,0, byteofimg4.length); arg0.drawImage(image1, x1, y1, Graphics.TOP|Graphics.LEFT); arg0.drawImage(image2, x2, y2, Graphics.TOP|Graphics.LEFT); arg0.drawImage(image3, x3, y3, Graphics.TOP|Graphics.LEFT); arg0.drawImage(image4, x4, y4, Graphics.TOP|Graphics.LEFT); } else { image3 = Image.createImage(byteofimg3,0, byteofimg3.length); image4 = Image.createImage(byteofimg4,0, byteofimg4.length); image5 = Image.createImage(byteofimg5,0, byteofimg5.length); image6 = Image.createImage(byteofimg6,0, byteofimg6.length); arg0.drawImage(image3, x3, y3, Graphics.TOP|Graphics.LEFT); arg0.drawImage(image4, x4, y4, Graphics.TOP|Graphics.LEFT); arg0.drawImage(image3, x5, y5, Graphics.TOP|Graphics.LEFT); arg0.drawImage(image4, x6, y6, Graphics.TOP|Graphics.LEFT); } } }
这段代码解决了:手机地图下载,手机地图随方向键的按下而移动.但是还是存在问题:如果用户只按一下方向键,地图移动 5 个象素,而按住住方向键不放同样也是移动 5 个象素.对于这种效果,用户肯定是不会满意的.然而按键事件只是通知按下和松开,仅仅改写按键事件方法,不能起到根据用户按键不放时间进行移动地图的效果.这个时间段怎么计算呢?作者想到了线程编程,问题迎刃而解.解决后的源码如下,读者可以把代码考到自己电脑上,运行一下,体会其中的奥妙.
ImageMIDlet.java 源码如下:
/** * 文件名:ImageMIDlet.java * * 版本信息: * 日期:2009-10-17 * Copyright XXX Corporation 2009 * 版权所有 * */ package image.downed.move; import javax.microedition.lcdui.Command; import javax.microedition.lcdui.CommandListener; import javax.microedition.lcdui.Display; import javax.microedition.lcdui.Displayable; import javax.microedition.midlet.MIDlet; import javax.microedition.midlet.MIDletStateChangeException; /** * * 项目名称:Imagedownedmove * 类名称:ImageMIDlet * 类描述:This is the main class of the image downed move. * 创建人:Defonds * 创建时间:2009-10-17 上午11:02:17 * 修改人:Defonds * 修改时间:2009-10-17 上午11:02:17 * 修改备注: * @version * */ public class ImageMIDlet extends MIDlet implements CommandListener { /** * The canvas is the region of the screen. */ ImageCanvas3 myCanvas; /** * The command objects appears as buttons. */ private Command exitCommand = new Command("退出",Command.EXIT,99); /** * Initialize the canvas and the commands. */ public ImageMIDlet() { myCanvas = new ImageCanvas3(Display.getDisplay(this)); myCanvas.addCommand(exitCommand); myCanvas.setCommandListener(this); } //------------------------------------- //Implimentation of MIDlet. /** * If the MIDlet was usring resources,it should release them in this method. */ /* (non-Javadoc) * @see javax.microedition.midlet.MIDlet#destroyApp(boolean) */ protected void destroyApp(boolean arg0) throws MIDletStateChangeException {} /** * This method is called to notify the MIDlet to enter a paused state. * The MIDlet should use this opportunity to release shared resources. */ /* (non-Javadoc) * @see javax.microedition.midlet.MIDlet#pauseApp() */ protected void pauseApp() { } /** * Start the application. */ /* (non-Javadoc) * @see javax.microedition.midlet.MIDlet#startApp() */ protected void startApp() throws MIDletStateChangeException { myCanvas.start(); } //--------------------------------- // implimentation of CommandListener. /** * Respond to a command issued on the Canvas. * (either reset or exit). */ /* (non-Javadoc) * @see javax.microedition.lcdui.CommandListener#commandAction(javax.microedition.lcdui.Command, javax.microedition.lcdui.Displayable) */ public void commandAction(Command arg0, Displayable arg1) { if(arg0 == exitCommand) { try { destroyApp(false); notifyDestroyed(); } catch (MIDletStateChangeException e) {} } } }
ImageCanvas3.java 源代码如下:
/** * 文件名:ImageCanvas3.java * * 版本信息: * 日期:2009-10-18 * Copyright XXX Corporation 2009 * 版权所有 * */ package image.downed.move; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Date; import javax.microedition.io.Connector; import javax.microedition.io.HttpConnection; import javax.microedition.lcdui.Canvas; import javax.microedition.lcdui.Display; import javax.microedition.lcdui.Graphics; import javax.microedition.lcdui.Image; /** * * 项目名称:Imagedownedmove * 类名称:ImageCanvas3 * 类描述:This class represents the region of the screen. * 利用 按键方法重写 + 线程 + 计时器 解决按住向下键不放手机地图移动的距离问题. * 创建人:Defonds * 创建时间:2009-10-18 上午10:33:03 * 修改人:Defonds * 修改时间:2009-10-18 上午10:33:03 * 修改备注: * @version * */ public class ImageCanvas3 extends Canvas implements Runnable { //---------------------------- //fields /** * A handle to the screen of the device. */ Display myDisplay; /** * A handle to the run method. */ private Thread myThread = null; /** * 按住向下建的单位时间内,图片向上移动的距离. */ private final int yspeed = 5; /** * 手机屏幕中心点坐标. */ private final int x = getWidth()/2,y = getHeight()/2; /** * 下载图片的宽度(假想全部为正方形图片). */ private final int imageWidth = 300; /** * 按键处理代码需要变量: * keyDown:Canvas.DOWN 按下为 true,反之为 false; * action:GameAction */ private boolean keyDown = false; private int action = 0; /** * 6 个图片的初始坐标. */ private int x1 = x - imageWidth,y1 = y - imageWidth; private int x2 = x,y2 = y - imageWidth; private int x3 = x - imageWidth,y3 = y; private int x4 = x,y4 = y; private int x5 = x - imageWidth,y5 = y + imageWidth; private int x6 = x,y6 = y + imageWidth; /** * 要画的图片. */ Image image1,image2,image3,image4,image5,image6; /** * 要下载的图片的 URL(规格: 300 X 300). */ String url1 = "http://img1.mapbar.com/maplite/mapbank/baidu/8/116_39/4_7.png"; String url2 = "http://img3.mapbar.com/maplite/mapbank/baidu/8/116_39/5_7.png"; String url3 = "http://img.mapbar.com/maplite/mapbank/baidu/8/116_39/6_6.png"; String url4 = "http://mappng.baidu.com/maplite/mapbank/baidu/5/11_4/3_3.png"; String url5 = "http://img3.mapbar.com/maplite/mapbank/baidu/8/109_42/1_1.png"; String url6 = "http://www.sunyn.com/kmtour/UploadFiles_7357/200706/20070625215831613.png"; /** * 要下载的图片的字节流. */ byte[] byteofimg1,byteofimg2,byteofimg3,byteofimg4,byteofimg5,byteofimg6,byteofimg7; //--------------------------------------- //Initialization. /** * Constructor merely sets the display. */ public ImageCanvas3(Display d) { super(); myDisplay = d; try { byteofimg1 = loadHttpFile(url1); System.out.println("byteofimg1.length="+byteofimg1.length); byteofimg2 = loadHttpFile(url2); System.out.println("byteofimg2.length="+byteofimg2.length); byteofimg3 = loadHttpFile(url3); System.out.println("byteofimg3.length="+byteofimg3.length); byteofimg4 = loadHttpFile(url4); System.out.println("byteofimg4.length="+byteofimg4.length); byteofimg5 = loadHttpFile(url5); System.out.println("byteofimg5.length="+byteofimg5.length); byteofimg6 = loadHttpFile(url6); System.out.println("byteofimg6.length="+byteofimg6.length); } catch (IOException e) { e.printStackTrace(); } } /** * This is called as soon as the application begins. */ public void start() { if(myThread == null) { myThread = new Thread(this); myThread.start(); } myDisplay.setCurrent(this); repaint(); } /** * This is called as soon as the application ends. */ public void stop() { if(myThread != null) { myThread = null; } } /** * 清屏. */ public void clearScreen(Graphics g) { g.setColor(255,255,255);//要设置的底色:白色 g.fillRect(0, 0, getWidth(), getHeight()); } /** * 键盘事件函数 keyPressed 的重写 */ protected void keyPressed(int keyCode) { action = getGameAction(keyCode); switch(action) { case Canvas.DOWN:keyDown = true; } } /** * 键盘事件函数 keyReleased 的重写 */ protected void keyReleased(int keyCode) { action = getGameAction(keyCode); switch(action) { case Canvas.DOWN: keyDown = false;//keyDown 复位. } } /** * http 下载图片函数. */ public byte[] loadHttpFile(String url) throws IOException { byte[] byteBuffer; HttpConnection hc = (HttpConnection) Connector.open(url); System.out.println("loadHttpFile:"+url); try { hc.setRequestMethod(HttpConnection.GET); InputStream is = hc.openInputStream(); try { int len = (int) hc.getLength(); if (len > 0) { byteBuffer = new byte[len]; int done = 0; while (done < len) { done += is.read(byteBuffer, done, len - done); } } else { ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] buffer = new byte[512]; int count; while ( (count = is.read(buffer)) >= 0 ) { bos.write(buffer, 0, count); } byteBuffer = bos.toByteArray(); } } finally { is.close(); } } finally { hc.close(); } return byteBuffer; } //----------------------- //Graphics method. /** * Clear the screen and display the image. */ /* (non-Javadoc) * @see javax.microedition.lcdui.Canvas#paint(javax.microedition.lcdui.Graphics) */ protected void paint(Graphics arg0) { clearScreen(arg0); image1 = Image.createImage(byteofimg1,0, byteofimg1.length); image2 = Image.createImage(byteofimg2,0, byteofimg2.length); image3 = Image.createImage(byteofimg3,0, byteofimg3.length); image4 = Image.createImage(byteofimg4,0, byteofimg4.length); image5 = Image.createImage(byteofimg5,0, byteofimg5.length); image6 = Image.createImage(byteofimg6,0, byteofimg6.length); arg0.drawImage(image1, x1, y1, Graphics.TOP|Graphics.LEFT); arg0.drawImage(image2, x2, y2, Graphics.TOP|Graphics.LEFT); arg0.drawImage(image3, x3, y3, Graphics.TOP|Graphics.LEFT); arg0.drawImage(image4, x4, y4, Graphics.TOP|Graphics.LEFT); arg0.drawImage(image5, x5, y5, Graphics.TOP|Graphics.LEFT); arg0.drawImage(image6, x6, y6, Graphics.TOP|Graphics.LEFT); } //--------------------------------- //implimentation of Runnable. /* (non-Javadoc) * @see java.lang.Runnable#run() */ public void run() { while(true) { if(keyDown) { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("向下键被按下,屏幕开始重绘!"); y1 = y1 - yspeed; y2 = y2 - yspeed; y3 = y3 - yspeed; y4 = y4 - yspeed; y5 = y5 - yspeed; y6 = y6 - yspeed; repaint(); } } } }