本文將向大家講述如何給予MIDP1.0實現手機通信錄,讀者需要具備J2ME的基本知識,了解它的構架和主要內容。開發工具選擇了eclipse+wtk2.1+j2sdk1.4.2+eclipseME。
在MIDP1.0中的javax.microedition.lcdui包內定義了21個類和3個接口,這比J2SE中的AWT和SWING要簡單很多。在這24個類中,Display是負責設備的顯示以及輸入的管理器,通常我們通過調用setCurrent(Displayable displayable)方法來把displayable組件顯示在手機屏幕上。Displayable代表了能夠在屏幕上顯示的組件對象,它的兩個抽象子類是Canvas和Screen,他們分別代表了MIDP中的低級用戶界面和高級用戶界面。
Form,Alert,List和TextBox都是從Screen繼承過來的,他們構成了MIDP中的高級用戶界面。要清楚他們每個組件都必須單獨占用一個屏幕,不能與其他組件放在一起。Form類在javax.microedition.lcdui包中至關重要,它是Item的容器,通過調用append(Item item)方法,你可以把TextField、DateField等Item放在Form內。例如下面的代碼:
public NewPhoneUI(UIController uicontroller)
{
super(Title.add_record);
this.uicontroller = uicontroller;
nameField = new TextField(Title.name, null, 25, TextField.ANY);
mobileField = new TextField(Title.mobile, null, 25,
TextField.PHONENUMBER);
choice = new ChoiceGroup(null, ChoiceGroup.MULTIPLE);
phoneField=new TextField(Title.phone,null,25,TextField.PHONENUMBER);
emailField=new
TextField(Title.email, null, 25, TextField.EMAILADDR);
choice.append(Title.detail, null);
this.append(nameField);
this.append(mobileField);
this.append(choice);
this.addCommand(saveCommand);
this.addCommand(backCommand);
this.setCommandListener(this);
this.setItemStateListener(this);
}
Canvas類代表了MIDP的低級用戶界面,它是一個抽象類。你需要繼承Canvas并實現它的抽象方法paint(Graphics g)來構建你自己的Canvas實例。Paint()方法中的參數g非常重要。因為通過它提供的方法你才能在屏幕上繪畫你的界面。如果有時間您應該多多研究一下Canvas類和Graphics類。在個人通信錄中我們提供了一個WaitCanvas類并通過它構建了Dialog組件。從下面的代碼中您能學會如何使用Canvas類。
package com.north.phonebook.ui;
import java.util.*;
import javax.microedition.lcdui.*;
public class WaitCanvas extends Canvas
{
PRivate int mCount, mMaximum;
private int mInterval;
private int mWidth, mHeight, mX, mY, mRadius;
private String mMessage;
private boolean run = false;
public WaitCanvas(String message, boolean run)
{
this.mMessage = message;
mCount = 0;
mMaximum = 36;
mInterval = 100;
mWidth = getWidth();
mHeight = getHeight();
// Calculate the radius.
int halfWidth = (mWidth - mRadius) / 2;
int halfHeight = (mHeight - mRadius) / 2;
mRadius = Math.min(halfWidth, halfHeight);
// Calculate the location.
mX = halfWidth - mRadius / 2;
mY = halfHeight - mRadius / 2;
// Create a Timer to update the display.
if (run)
{
TimerTask task = new TimerTask()
{
public void run()
{
mCount = (mCount + 1) % mMaximum;
repaint();
}
};
Timer timer = new Timer();
timer.schedule(task, 0, mInterval);
}
}
public void setMMessage(String message)
{
mMessage = message;
}
public void paint(Graphics g)
{
int theta = -(mCount * 360 / mMaximum);
g.setColor(255, 255, 255);
g.fillRect(0, 0, mWidth, mHeight);
g.setColor(128, 128, 255);
g.drawArc(mX, mY, mRadius, mRadius, 0, 360);
g.fillArc(mX, mY, mRadius, mRadius, theta + 90, 90);
g.fillArc(mX, mY, mRadius, mRadius, theta + 270, 90);
if (mMessage != null)
{
g.drawString(mMessage,mWidth/2,mHeight,Graphics.BOTTOM
Graphics.HCENTER);
}
}
}
下面我們看看MIDP中的事件處理機制,它同樣分為高級事件處理和低級事件處理。高級事件處理由Command和Item事件組成。他們分別對應CommandListener和ItemStateListener接口。你可以在Displayable組件上添加Command并實現CommandListener接口。這個接口只定義了一個方法commandAction(Command cmd,Displayable),因此你要實現這個接口告訴應用程序當指定的command按下的時候它應該去執行什么操作,當然你不能忘記了注冊Listener。例如:
public void commandAction(Command arg0, Displayable arg1)
{
if(arg0 == backCommand)
{
uicontroller.handleEvent
(UIController.EventID.EVENT_SEARCHUI_BACK_MAINNUI);
}
else if(arg0 == searchCommand)
{
String userName = inputField.getString();
if(userName.length()!= 0)
{
uicontroller.handleEvent(UIController.EventID.EVENT_SEARCH_RECORD_ANYWAY,new Object[]{userName});
}
}
}
ItemStateListener定義的方法是itemStateChanged(Item item),它的含義是當指定的item的內容發生變化的時候告訴應用程序去執行相應的操作,例如當TextField中用戶輸入了姓名,那么應用程序去RMS中去查詢相關的記錄并返回。例如
public void itemStateChanged(Item item)
{
if(item == inputField)
{
String userName = inputField.getString();
if(userName.length()!= 0)
{
uicontroller.handleEvent(UIController.EventID.EVENT_SEARCH_RECORD,new Object[]{userName});
}
}
}
MIDP中的低級事件處理是通過實現Canvas類的相關方法來實現的,例如當用戶按下某個按鍵,應用程序應該去處理相應的操作。由于個人通信錄中并未涉及相關內容因此不做講解。
MIDP中的UI類使用起來比不難,然而界面導航問題卻并不容易解決,事實上它是困擾很多J2ME程序員的問題。在MIDP中我們只能通過調用Display類中的setCurrent()方法來實現不同界面之間的切換,如果界面多起來比如有8-10個界面的時候就會顯得非常的麻煩。你也許想構造一個樹形的結構來記錄每個界面的父親界面例如:
public ChildUI(Displayable parent,Dispaly display)
{
this.parent = parent;
this.display = display;
}
但是當界面以及相互之間的聯系增加的時候,界面的導航問題仍然是一個噩夢。MVC設計模式在Web application應用開發方面已經被證明是非常成功的,例如Apache的開源項目struts,在本文中我將講述如何應用MVC設計模式解決MIDP應用程序的界面導航問題。
MVC的目的就是實現顯示(View)與邏輯(Model)的分離,而在其中起到重要作用的就是控制器(Controller)。在控制器內通常我們要定義一些事件的代號以便和UI類通信,保證正確處理相應的事件,我們可以使用內部類來標記這些事件的代號。
public static class EventID
{
private EventID()
{
}
public static final byte EVENT_NEW_RECORD_SELECTED = 1;
public static final byte EVENT_SAVE_RECORD_SELECTED = 2;
public static final byte EVENT_NEWPHONE_BACK_MAINUI = 3;
public static final byte EVENT_LISTPHONE_BACK_MAINUI = 4;
public static final byte EVENT_SEARCHUI_BACK_MAINNUI = 5;
public static final byte EVENT_CLEAR_RECORD_YES = 6;
public static final byte EVENT_CLEAR_RECORD_NO = 7;
public static final byte EVENT_DELETE_RECORD = 8;
public static final byte EVENT_DELETE_RECORD_YES = 9;
public static final byte EVENT_DELETE_RECORD_NO = 10;
public static final byte EVENT_DISPLAY_INFOMATION = 11;
public static final byte EVENT_DETAIL_BACK_LIST = 12;
public static final byte EVENT_SEARCH_RECORD = 13;
public static final byte EVENT_SEARCH_RECORD_ANYWAY = 14;
public static final byte ADD_NEW_RECORD = 100;
public static final byte SEARCH_RECORD = 101;
public static final byte CLEAR_RECORD = 102;
public static final byte LIST_RECORD = 103;
public static final byte HELP = 104;
}
當UI類中有事件發生的時候它可以向UIController類傳輸事件的代碼,UIController類根據代碼來進行相應的事件處理。例如:
if (arg0 == backCommand)
{
uicontroller.handleEvent(UIController.EventID.EVENT_NEWPHONE_BACK_MAINUI)
}
UIController的handleEvent()方法則在接收到UI類的請求之后調用Model類的相關方法得到響應,然后再顯示相關的界面。
public void handleEvent(int eventID)
{
switch (eventID)
{
case EventID.ADD_NEW_RECORD:
{
newPhoneUI.clear();
display.setCurrent(newPhoneUI);
break;
}
case EventID.CLEAR_RECORD:
{
dialog.setMessage(Title.delete_phonebook);
dialog.display(EventID.CLEAR_RECORD);
break;
}
case EventID.EVENT_CLEAR_RECORD_YES:
{
try
{
model.clearAllRecord();
display.setCurrent(indexFunctionUI);
} catch (ApplicationException e)
{
e.printStackTrace();
}
break;
}
……
……
}
有的時候我們不光要告訴控制類要做什么還要傳輸給他一些從界面類采集到的數據,這時候我們可以在UIController類中重載handleEvent()方法,添加一個Object[]類型的參數來接收數據,如下所示:
public void handleEvent(int eventID, Object[] obj)
{
switch (eventID)
{
case EventID.EVENT_SAVE_RECORD_SELECTED:
{
try
{
Account account = (Account) obj[0];
if (model.isRecordExist(account.getUserName()))
{
showAlert(Title.record_exist, indexFunctionUI,
AlertType.WARNING);
} else
{
model.addRecord(account);
showAlert(Title.record_added, indexFunctionUI,
AlertType.CONFIRMATION);
}
} catch (ApplicationException e)
{
e.printStackTrace();
}
break;
}
}
}
例如在添加新電話記錄的時候,我們可以這樣實現commandAction()方法向UIController傳送消息。
if (arg0 == saveCommand)
{
……
……
Account newAccount = new Account(userName, mobilePhone, phone,
email);
uicontroller.handleEvent(
UIController.EventID.EVENT_SAVE_RECORD_SELECTED,
new Object[] { newAccount });
}
我們很難保證用戶輸入的數據有效,也很難保證用戶的操作都合理,因此我們必須針對用戶的不合理的操作給出相對的提示或者警告。在javax.microedition.lcdui包中Alert類能夠很好的完成這個任務,因此我們自己提供一個方法如下所示:
public void showAlert(String message, Displayable next, AlertType type)
{
alert = new Alert(Title.alertTitle, message, null, type);
alert.setTimeout(1500);
setCurrent(alert, next);
}
當不合理的事件發生的時候我們應該調用它。
String userName = nameField.getString();
if (userName.length() == 0)
{
uicontroller.showAlert(Title.userNameNull, this,AlertType.WARNING);
return;
}
例如當用戶并沒有輸入姓名就按下了保存的按鈕的時候,應該提示用戶“用戶名不能為空”。
有些時候,某些操作可能會被堵塞,例如聯網或者從RMS中讀取大量的數據,這個時候我們應該使用多線程,多線程是java語言中內嵌的特性,使用起來也非常簡單。在本例中當我們瀏覽的電話本中包含很多數據的時候,如果不使用多線程,主界面會持續幾秒鐘不動,這對用戶來說很不友好,因為用戶不知道現在應用程序在做什么,在這個時候使用多線程就顯得非常必要。
本文從介紹J2ME平臺,搭建開發環境到最后發布應用程序,詳細的介紹了J2ME的開發過程,其中對MIDP的用戶界面和Record Management System做了詳細、深入的分析。這是本人在進行J2ME開發的一點經驗和體會,希望和讀者一起分享。由于水平有限,錯誤在所難免,歡迎大家批評指正
(出處:http://www.companysz.com)
新聞熱點
疑難解答