基于MIDP1.0实现通信录

作者:mingjava  文章来源:http://www.j2medev.com/Article/ShowArticle.asp?ArticleID=41

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

 

 

 

 

 

 

 

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

以下是对提供的参考资料的总结,按照要求结构化多个要点分条输出: 4G/5G无线网络优化与网规案例分析: NSA站点下终端掉4G问题:部分用户反馈NSA终端频繁掉4G,主要因终端主动发起SCGfail导致。分析显示,在信号较好的环境下,终端可能因节能、过热保护等原因主动释放连接。解决方案建议终端侧进行分析处理,尝试关闭节电开关等。 RSSI算法识别天馈遮挡:通过计算RSSI平均值及差值识别天馈遮挡,差值大于3dB则认定有遮挡。不同设备分组规则不同,如64T和32T。此方法可有效帮助现场人员识别因环境变化引起的网络问题。 5G 160M组网小区CA不生效:某5G站点开启100M+60M CA功能后,测试发现UE无法正常使用CA功能。问题原因在于CA频点集标识配置错误,修正后测试正常。 5G网络优化与策略: CCE映射方式优化:针对诺基亚站点覆盖农村区域,通过优化CCE资源映射方式(交织、非交织),提升RRC连接建立成功率和无线接通率。非交织方式相比交织方式有显著提升。 5G AAU两扇区组网:与三扇区组网相比,AAU两扇区组网在RSRP、SINR、下载速率和上传速率上表现不同,需根据具体场景选择适合的组网方式。 5G语音解决方案:包括沿用4G语音解决方案、EPS Fallback方案和VoNR方案。不同方案适用于不同的5G组网策略,如NSA和SA,并影响语音连续性和网络覆盖。 4G网络优化与资源利用: 4G室分设备利旧:面对4G网络投资压减与资源需求矛盾,提出利旧多维度调优策略,包括资源整合、统筹调配既有资源,以满足新增需求和提质增效。 宏站RRU设备1托N射灯:针对5G深度覆盖需求,研究使用宏站AAU结合1托N射灯方案,快速便捷地开通5G站点,提升深度覆盖能力。 基站与流程管理: 爱立信LTE基站邻区添加流程:未提供具体内容,但通常涉及邻区规划、参数配置、测试验证等步骤,以确保基站间顺畅切换和覆盖连续性。 网络规划与策略: 新高铁跨海大桥覆盖方案试点:虽未提供详细内容,但可推测涉及高铁跨海大桥区域的4G/5G网络覆盖规划,需考虑信号穿透、移动性管理、网络容量等因素。 总结: 提供的参考资料涵盖了4G/5G无线网络优化、网规案例分析、网络优化策略、资源利用、基站管理等多个方面。 通过具体案例分析,展示了无线网络优化中的常见问题及解决方案,如NSA终端掉4G、RSSI识别天馈遮挡、CA不生效等。 强调了5G网络优化与策略的重要性,包括CCE映射方式优化、5G语音解决方案、AAU扇区组网选择等。 提出了4G网络优化与资源利用的策略,如室分设备利旧、宏站RRU设备1托N射灯等。 基站与流程管理方面,提到了爱立信LTE基站邻区添加流程,但未给出具体细节。 新高铁跨海大桥覆盖方案试点展示了特殊场景下的网络规划需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值