基于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开发的一点经验和体会,希望和读者一起分享。由于水平有限,错误在所难免,欢迎大家批评指正

深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值