除了游戲程序,在通常的MIDP應用程序中,通常會有很多個Screen或Canvas,這些屏幕一般靠命令來實現切換,比如用戶點擊“Next”應該跳到下一屏,點擊“Back”應該返回到上一屏。當屏幕數量相當可觀時,如何在各個屏幕之間導航就值得好好考慮了。
<!-- frame contents --> <!-- /frame contents -->經典的MVC模式可用於屏幕導航,Model用於存儲應用程序數據,而View則是各個Displayable對象,Controller需要單獨的一個類實現。由於MIDlet類本身在生命周期內就只有一個實例,因此MIDlet類就非常適合作為Controller。SUN在blueprints示例程序SmartTicket中應用了非常復雜的MVC,完全可以滿足MIDP應用程序的導航需要,但是可以看出,缺點是很明顯的:
一是每一個事件都需要一個唯一標識,switch-case語句會隨著屏幕的增加而增加,Controller變得難以維護。二是Controller引用了所有的View,這些View在程序啟動時就被初始化導致很大的內存開銷,而不管它們是否會被顯示。三是大量的Model對象以及異常處理都使得整個應用程序的邏輯大大復雜。
實際上,MIDP應用程序的很多屏幕並不需要復雜的Controller和Model,我們的目標是滿足基本的靈活性的同時保持結構簡單。因此,另外兩種導航方法是用二叉樹和堆棧實現,這裡我們只討論用堆棧實現的MIDP導航框架,其基本思想是:每當前進到下一個屏幕時,先將下一個屏幕壓棧,然後再顯示;當返回到上一個屏幕時,先從堆棧中彈出當前屏幕,再從堆棧中取出上一個屏幕並顯示。因此,每個屏幕只需要指定要顯示的下一個屏幕,而不需記住上一個屏幕。這種堆棧導航模型非凡適合有規律的“前進”、“後退”屏幕。
由於MIDlet類運行期只有一個實例,因此,使用MIDlet類作為控制器相當合適。此外,我們在一個靜態變量中保存了MIDlet實例,使得訪問MIDlet更加方便:
public class ControllerMIDlet extends MIDlet {
private static ControllerMIDlet instance = null;
private Display display = null;
private Stack ui = new Stack();
public ControllerMIDlet() { instance = this; }
protected void startApp() {}
protected void pauseApp() {}
protected void destroyApp(boolean unconditional) {}
public static void goBack() {
instance.ui.pop();
Object obj = instance.ui.peek();
instance.display.setCurrent((Displayable)obj);
}
public static void forward(Displayable next) {
instance.ui.push(next);
instance.display.setCurrent(next);
}
}
讓我們更具體地研究一下實際的應用程序可能出現的幾種屏幕跳轉情況。最簡單的情況是,從一個屏幕前進到另一個屏幕,且返回時仍回到原先的屏幕,這種情況完全符合堆棧的FIFO特點,可以直接調用ControllerMIDlet的forward和goBack方法即可。例如,要顯示一個幫助屏幕:
對於一個聯網的應用程序,另一種情況是有一個暫時的等待屏幕。下面是一個在線浏覽圖片的屏幕:
與上面的情況所不同的是,假如用戶在屏幕3選擇“返回”,則應當回到屏幕1而不是屏幕2,因此,對於屏幕2到屏幕3的切換,就不能forward,我們使用replace,拋棄屏幕2,從而實現屏幕3直接可以goBack到屏幕1:
public static void replace(Displayable next) {