因为移动设备运行速度问题,J2ME开发比较注重程序的复杂度。为了寻求时间复杂度与空间复杂度的一个平衡,通常将程序分为多个页面。多个页面就需要一个管理工具。本文展示了一种经典的J2ME中管理多个页面的调度器。
为每一个页面编写一个类,在每一个类中实现以下方法
//无参的构造方法 //执行的run方法 public void run() {} //绘图的paint方法,参数为屏幕的画笔 public void paint(Graphics g) {} //按键时的响应方法,参数为按键消息码 public void keyPressed(int key) {}
调度器应该继承Canvas类,实现Runnable接口,完成以下功能
1、changeState方法
负责一个页面到另一个页面的切换。为了节约资源,应该根据旧的状态量释放当前页面,再根据请求的新状态创建另一个页面。
2、run方法
根据状态量判断当前哪个页面,并调用它(当前页面)的run方法。
3、paint方法
调用当前页面的paint(绘图)方法
4、keyPressed方法
调用当前页面的keyPressed(键盘相应)方法
这些是基本的功能,还可以根据需要添加更多的Canvas类中的方法,实现更多功能。
这里演示了ModelLogo,ModelMenu和ModelGame三个页面的切换
public class ZYMIDlet extends MIDlet { static ZYCanvas canvas = null; static ZYMIDlet midlet = null; /**初始动作 不要在构造函数中进行 可能出现问题 * 这主要是因为移动设备的资源性能都有限, * 因此此种程序开发的一个宗旨是“在需要用资源的时候申请,用完立刻释放” * */ public ZYMIDlet() { midlet = this; } /**大多数初始化工作是放在startApp中完成 * midlet创建的时候 执行完构造函数,会调用此函数,midlet进入活跃状态 * midlet从暂停状态 恢复的时候 也会自动调用此函数*/ public void startApp() { if(canvas == null) { canvas = new ZYCanvas(); } Display display = Display.getDisplay(this); display.setCurrent(canvas); } public void pauseApp() {} public void destroyApp(boolean unconditional) {} } class ZYCanvas extends Canvas implements Runnable { public static final byte STATE_LOGO = 0; public static final byte STATE_MENU = 1; public static final byte STATE_GAME = 2; public static int state; public ModelLogo modelLogo; public ModelMenu modelMenu; public ModelGame modelGame; public ZYCanvas() { state = STATE_LOGO; //开启线程 changeState(STATE_LOGO); } /**改变状态:这里可以做一些 上个状态的模块清除资源工作,和 新状态模块的初始化工作*/ public static void changeState(byte new_state) { //可做的事情:清除上一个模块的资源 state = new_state; //可作的事情:创建新状态模块,初始化 } //每80毫秒执行一次 public void run() { long time = System.currentTime(); action(); repaint(); //通知调用paint()函数 serverPaint(); //强制paint执行 long dur = System.currentTime() - time; long test = 80 - dur if(test > 0){ try{Thread.sleep(test);}catch(Exception e){} } } //可以把要做的事情全部放到一个函数中 public void action() { switch(state) { case STATE_LOGO: modelLogo.run();break; case STATE_MENU: modelMenu.run();break; case STATE_GAME: modelGame.run();break; } } //可以把要做的事情全部放到一个函数中 public void paint(Graphics g) { switch(state) { case STATE_LOGO: modelLogo.paint(g);break; case STATE_MENU: modelMenu.paint(g);break; case STATE_GAME: modelGame.paint(g);break; } } public void keyPressed(int key) { switch(state) { case STATE_LOGO: modelLogo.keyPressed(key);break; case STATE_MENU: modelMenu.keyPressed(key);break; case STATE_GAME: modelGame.keyPressed(key);break; } } } /**封装模块*/ class ModelLogo { public ModelLogo(){} public void run() { //运行自己的逻辑 //如果这个状态结束可以调用ZYCanvas.changeState()来改变状态 } public void paint(Graphics g){} public void keyPressed(int key){} } class ModelMenu { public ModelGame(){} public void run() { //运行自己的逻辑 //如果这个状态结束可以调用ZYCanvas.changeState()来改变状态 } public void paint(Graphics g){} public void keyPressed(int key){} } class ModelGame { public ModelGame(){} public void run() { //运行自己的逻辑 //如果这个状态结束可以调用ZYCanvas.changeState()来改变状态 } public void paint(Graphics g){} public void keyPressed(int key){} }
通过调度器只需要调用changeState方法就可以在页面直接进行切换。但是实际应用时调度器有一些不足
我们想添加一个页面,需要声明一个状态量,再页面相应的类,然后在调度器每个方法中添加对于新的页面要执行的操作。
很明显的,不符合封装的设计思想。
怎样把调度器封装起来呢?
很明显,我们要解决的最主要的问题是当调度器编写时,并不知道未来有一个什么样的页面类将要供调度器调度。
该如何使用未知的类?
我们可以应用java语言反射机制动态加载类来解决这类问题。通常,我们想要创建一个对象,我们采用以下方法
Class c = new Class();
java提供了动态加载类的机制,在运行时通过一个表示类名的字符串查找一个类并加载,还可以产生它的一个实例。使用方法如下
getClass().forName("package.className").newInstance();
只要创建一个调度器对象,再将页面类的类名添加进调度器,调度器使用动态加载类创建页面对象就可以了
通过动态加载类创建的对象是Object类型的,显然这是没有我们想要的run,paint,keyPressed方法的,要通过强制类型转换把Object类型对象装换成含有这些方法的对象。
把它转换成什么类型对象合适,又如何保证用户编写的页面类肯定有这些方法呢?
通过java的接口来保证!
下面编写一个接口,规定用户编写的所有页面必须实现这个接口
package com.shiying.frame; import javax.microedition.lcdui.Graphics; //页面接口 public interface Model { public void run(); public void paint(Graphics g); public void keyPressed(int key); }
要使用动态加载类,应该保存每个页面类的类名字符串,同时还要保存每个页面所对应的状态量。最简单的使用集合来保存
private static Vector stateVector = new Vector(); private static Vector nameVector = new Vector();
为了调用当前类的方法,还要保存当前类的状态和对当前类对象的引用,这个页面应该是Model类型
private static Model model = null; private static byte state;
下面编写一个方法,用来向调度器注册页面
public void addModel(byte stateId, String modelClassName) { stateVector.addElement(new Byte(stateId)); nameVector.addElement(modelClassName); }
当用户发出切换页面的请求后,应该释放当前页面,并启动新的页面
/* * 通过状态查找在表中的位置 */ private static int searchState(byte state) { for (int n=0; nif (((Byte)Framework.stateVector.elementAt(n)).byteValue() == state) { return n; } } return -1; } /**改变状态:这里可以做一些 上个状态的模块清除资源工作,和 新状态模块的初始化工作*/ public static void changeState(byte new_state) { //清除上一个模块的资源 int index = searchState(state); if (index != -1) model = null; //状态转换 state = new_state; //创建新状态模块,初始化 index = searchState(new_state); if (index != -1) { String name = (String)Framework.nameVector.elementAt(index); Class c = null; try { c = Class.forName(name); model = (Model) c.newInstance(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }
因为每个时刻至多有一个页面对象,所以不需要判断状态,对页面函数调用就非常简单
//每80毫秒执行一次 public void run() { while (true) { long time = System.currentTimeMillis(); action(); repaint(); //通知调用paint()函数 serviceRepaints(); //强制paint执行 long dur = System.currentTimeMillis() - time; long test = 80 - dur; if(test > 0){ try { Thread.sleep(test); } catch (InterruptedException e) { e.printStackTrace(); } } } } //可以把要做的事情全部放到一个函数中 public void action() { if (model != null) { model.run(); } } //可以把要做的事情全部放到一个函数中 public void paint(Graphics g) { if (model != null) { model.paint(g); } } public void keyPressed(int key) { if (model != null) { model.keyPressed(key); } }
编写一个启动调度器的方法,需要指明开启的第一个页面
public void go(byte start) { //开启线程 state = start; changeState(start); Thread th = new Thread(this); th.start(); }
OK,改装结束,整理一下代码
// Main.java ////////////////////////////////////// package com.shiying.frame; import javax.microedition.lcdui.Display; import javax.microedition.midlet.MIDlet; import com.shiying.Application.Frame; public class Main extends MIDlet{ static Frame canvas = null; static Main midlet = null; public Main() { midlet = this; } public void startApp() { if(canvas == null) { canvas = new Frame(); } Display display = Display.getDisplay(this); display.setCurrent(canvas); } public void pauseApp() {} public void destroyApp(boolean unconditional) {} public static void shutDown() { midlet.notifyDestroyed(); } } // Model.java ////////////////////////////////////// package com.shiying.frame; import javax.microedition.lcdui.Graphics; //页面接口 public interface Model { public void run(); public void paint(Graphics g); public void keyPressed(int key); } // Framework.java ////////////////////////////////////// package com.shiying.frame; import java.util.Vector; import javax.microedition.lcdui.Canvas; import javax.microedition.lcdui.Graphics; public class Framework extends Canvas implements Runnable { public static Framework canvas = null; private static Vector stateVector = new Vector(); private static Vector nameVector = new Vector(); private static Model model = null; private static byte state; public Framework() { Framework.canvas = this; } public void go(byte start) { //开启线程 state = start; changeState(start); Thread th = new Thread(this); th.start(); } public void addModel(byte stateId, String modelClassName) { stateVector.addElement(new Byte(stateId)); nameVector.addElement(modelClassName); } /* * 通过状态查找在表中的位置 */ private static int searchState(byte state) { for (int n = 0; n < Framework.stateVector.size(); n++) { if (((Byte) Framework.stateVector.elementAt(n)).byteValue() == state) { return n; } } return -1; } /**改变状态:这里可以做一些 上个状态的模块清除资源工作,和 新状态模块的初始化工作*/ public static void changeState(byte new_state) { //清除上一个模块的资源 int index = searchState(state); if (index != -1) model = null; //状态转换 state = new_state; //创建新状态模块,初始化 index = searchState(new_state); if (index != -1) { String name = (String)Framework.nameVector.elementAt(index); Class c = null; try { c = Class.forName(name); model = (Model) c.newInstance(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } //每80毫秒执行一次 public void run() { while (true) { long time = System.currentTimeMillis(); action(); repaint(); //通知调用paint()函数 serviceRepaints(); //强制paint执行 long dur = System.currentTimeMillis() - time; long test = 80 - dur; if(test > 0){ try { Thread.sleep(test); } catch (InterruptedException e) { e.printStackTrace(); } } } } //可以把要做的事情全部放到一个函数中 public void action() { if (model != null) { model.run(); } } //可以把要做的事情全部放到一个函数中 public void paint(Graphics g) { if (model != null) { model.paint(g); } } public void keyPressed(int key) { if (model != null) { model.keyPressed(key); } } }
首先编写的所有页面需要实现Model接口,并且具有公有的无参构造方法
package com.shiying.Application; import javax.microedition.lcdui.Graphics; import com.shiying.frame.Model; /* * 这是一个页面的示例,每一个页面必须实现com.shiying.frame.Model接口 */ public class ModelDemo implements Model { /* * 页面必须具有一个共有的无参构造方法 */ public ModelDemo() { } /* * 这个方法中添加页面对按键消息的响应过程 */ public void keyPressed(int key) { // TODO Auto-generated method stub } /* * 这个方法实现页面绘图过程 */ public void paint(Graphics g) { // TODO Auto-generated method stub System.out.println("Demo paint"); g.drawString("Frame Demo", Frame.canvas.getWidth() / 2, 100, Graphics.HCENTER|Graphics.TOP); } /* * 这个方法实现页面要执行的操作 */ public void run() { // TODO Auto-generated method stub System.out.println("Demo run"); } }
下面编写一个调度器类,这个类是Framework类的子类
这个类需要完成的工作有
1、声明每个页面的状态量
2、注册页面类
3、启动第一个页面
package com.shiying.Application; import com.shiying.frame.Framework; public class Frame extends Framework { /* * 这里声明代表每个页面的状态量(页面ID) */ public static final byte STATE_DEMO = 0; public Frame() { super(); /* * 这里添加页面,需要传入两个参数,页面ID和相应页面类所在位置 */ this.addModel(Frame.STATE_DEMO, "com.shiying.Application.ModelDemo"); /* * 这里设置第一个活跃的页面 */ this.go(Frame.STATE_DEMO); } }
当这个类被创建时,第一个页面会被自动调用。在第一个页面中可以根据需要调用changeState可以切换到下一个页面。
原文地址:http://www.cnblogs.com/S-E-P/archive/2010/10/11/2045052.html