分享
 
 
 

基于MIDP1.0实现通信录

王朝other·作者佚名  2008-05-31
窄屏简体版  字體: |||超大  

项目简介

基于MIDP1.0实现的个人通信录是我在学习MIDP子系统Record Management System的时候自己编写的应用程序,整个应用程序涉及到MIDP的高级和低级API、应用MVC实现界面导航、RMS的高级应用、多线程等知识。是学习J2ME开发不错的范例。由于本站有较多的文章介绍RMS,因此本文对开发中的部分问题进行了介绍。如果您有兴趣,可以直接下载源文件研究代码。如果提供下载请注明作者和出处

作者简介:

詹建飞(mingjava),北京邮电大学信息工程学院信号与信息处理专业研究生。

电子信箱:eric.zhan@263.net

本文将向大家讲述如何给予MIDP1.0实现手机通信录,读者需要具备J2ME的基本知识,了解它的构架和主要内容。开发工具选择了eclipse+wtk2.1+j2sdk1.4.2+eclipseME。

关于开发环境请参考搭建J2ME开发环境

关于J2ME的体系结构请参考J2ME平台的体系结构

精通MIDP用户界面设计

个人通信录提供了添加记录、浏览记录、删除记录、删除电话本、查找记录等功能。图4是几个主要界面的截图。细心的读者可能发现这里没有提供编辑的功能,读者可以免费得到个人通信录的源代码,这样您可以尝试添加这项功能。多读代码、多写代码是提高水平、掌握知识最快捷的途径。

在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类的相关方法来实现的,例如当用户按下某个按键,应用程序应该去处理相应的操作。由于个人通信录中并未涉及相关内容因此不做讲解。

应用MVC设计模式实现界面导航

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语言中内嵌的特性,使用起来也非常简单。在本例中当我们浏览的电话本中包含很多数据的时候,如果不使用多线程,主界面会持续几秒钟不动,这对用户来说很不友好,因为用户不知道现在应用程序在做什么,在这个时候使用多线程就显得非常必要。

关于RMS子系统的详细介绍请参考本站专题精通MIDP子系统RMS

本文从介绍J2ME平台,搭建开发环境到最后发布应用程序,详细的介绍了J2ME的开发过程,其中对MIDP的用户界面和Record Management System做了详细、深入的分析。这是本人在进行J2ME开发的一点经验和体会,希望和读者一起分享。由于水平有限,错误在所难免,欢迎大家批评指正

(出处:http://www.knowsky.com)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有