J2ME–Mail客户端

从上周一开始,琢磨着做一个类似G-Mail那样的手机客户端。


做这个客户端要通用各种Pop3协议的mail服务,用户使用输入pop,smtp参数就可以。
最大的问题是如果用cmwap上网,cmwap无法直接像mail服务器建立链接;如果用cmnet
费用又太贵了,取了一个折中的办法,上网方式用户可以选择cmwap,cmnet。程序本身不直接
链接mail服务器,而是向我的一个中间服务器发送各种指令信息,让那个中间服务器去向远程的
mail服务器来发送命令,这样一来中间服务器还可以当作cache机器来处理。
客户端和中间服务器通讯的方式用的是http协议。这样无论cmwap,cmnet都可以用。

 

我在模拟器上和Nokia N70,MOTO A1200 (手写)上测试通过(注一)。

这个程序的核心放在UI的设计上,UI设计还是继承自以前的自己定义的框架。这次又增加了
KCheckBox,KTextBox,KAlert等重要控件。
整个系统的风格也模仿WinCE的风格:)
整个程序的控件没有用到系统任何高级控件,所有控件都是我自己手画出来的。放在任何手机上
都是这个效果.(注二)
绘制控件是个非常细致的活,很多都是细节上的事情。当然也需要一定的技巧:)
主要功能:
1)收发Pop3协议的email;
2)不能砍信;
3)提供日历,发短信辅助工具
4)目前只支持一个账户同时使用,其实也可以像outlook那样建立多个账户;
5)可以自己定义菜单习惯和mail列表显示方式;
6)Mail 转发,回复等这些功能不用说了;
7)收下来的mail没有从服务器端删除,因为你可能会在其它地方用到,也没有标记未读(当然也可以);

手机DownLoad 地址:

http://211.136.85.162/onl/m-mail.jad

 

还是看图吧。

 

启动界面

Pop3,Smtp设置

Smtp设置保存后

设置菜单显示方式和mail列表显示项

内置工具SMS,Calendar

设置完毕,选择Login后

load完后显示列表…

选中mail某项后的Menu

转发界面

Mail详细界面(此mail没有内容)

贴图就看这些吧。真实手机上截图比较麻烦,还要用照相机照,看模拟器的方便一些:)

注一:关于手写笔,严格上说整个系统是半实现手写笔。一般手写笔手机没有左右软键,所以实现了左右软件的手写笔模拟
而其它控件上没有实现,可以通过UP,DOWN来切换焦点.其实也可以实现,只是细致一些,多写点Code.

注二:需要用到输入法的控件是通过一个高级控件和我这个控件相互交互的,详见以下文章;
不一定是在所有机器上都这个效果,可能有些机器上全屏控制会有些问题或者其它问题吧,对于屏幕尺寸较小,以及分辨率较低的手机可能看到的效果较糟糕。

PS更新:

增加了多个帐号的支持。
1.目前系统支持3个帐号。
2.增加对POP/SMTP SSL的支持。
3 阅读信内容时候增加转发到短信功能。
注:界面已经与上面的界面有些变化.

在调试过程中发现有一个非常隐藏的问题,就是通过HTTP通讯时得到的OutputStream,如果象里面写数据时,有些手机(LENOVO-S9)调用out.flush(),就可能导致堵塞的现象。后来在网上找到资料说有些手机确实有这个问题,开始以为我的手机拨号有问题呢。
下面是原文:

http://java2me.org/forum/viewtopic.php?p=340&sid=ce30a6cc0fdec3b619051f0e52e449c6

/*
The call to flush() uses a feature of HTTP 1.1 that allows data to be
sent in smaller loads. When calling flush() or sending a large
amount of data (in version 1.0.3) chunked encoding is used.

If you are using an HTTP 1.0 server or proxy server the chunked
transfer may cause problems.

You can avoid the chunking behavior with small transactions by just
calling close() where you were using flush(). For larger transactions
you need to buffer your output so a single write() and close() are
issued to the output stream.
*/

增加几个快捷键操作.

J2ME自己画输入框界面

前言:

之前写的功能中没有实现触摸屏,今天完成了触摸屏的一些操作。

其实只要实现pointerPressed(int x,int y); pointerReleased(int x,int y);然后根据当前的x,y计算出当前是哪个控件获得焦点,Pressed的意思是操作笔点下去出发的事件,当操作笔离开的时候出发Released事件。

控制好以上两个函数,计算出x,y然后获得当前活跃的对象,就可以了。实现起来比key事件稍微麻烦一些。
打开Eclipse看看J2ME,想了如何实现这个输入界面的问题了。:)

继承自Canvas的类,如果想直接调用输入法是不可能。如果想获得英文获得数字,可以通过捕获键盘事件来获得,如果你有字典库通过几个键的组合,也可以获得一个输入法,但是全世界那么多种语言,每个语言可能有不同的输入法,拿中文来说,就有拼音,五笔等输入法。你不可能在你的应用中把输入法这块花费那么大的时间,当然除非你是做输入法的。在某些特殊情况下,比如我想在手机上产生俄罗斯文字,而且不需要系统额外安装俄罗斯输入法(暂且这么叫吧),我可以根据俄文字典,根据用户当前按键来产生俄文字符。这样是可以的。

我们今天说的是另一个思路。

我们用界面的外观来模仿他,通过键盘时间来改变当前的控件。比如系统提供的TextBox是可以改变输入法的。

首先,在我们的界面上画出你的布局。输入框边界用灰色画出,有焦点时候用蓝色画出。

比如我的界面可能有两个输入框一个是手机号码字段,一个是内容字段。
我定义如下参数

TextBox[] m_textBoxs = new TextBox[2];
//两个控件对应的字符串,打开TextBox时把对应的字符串付给控件,完成输入的时候把控件的文字放到对应的字符串里面。
String[] m_strValues = new String[2];
//移动UP,DOWN 来控制当前选中的控件。
int p_currentIndex=-1;

为每个TextBox放置两个Command,”完成”,”后退”

图一:启动发送短信界面

(图二:这个界面都是画出来的,尽量模仿吧)

(图三:移动UP,DONW键效果,当前选中内容)


(图四 选中内容框,按下ok键,显示对应的TextBox,可以在里面输入)

(图五:按下完成键,真实手机还有一个界面,模拟器上没有,此时把控件里面的字符串放置到数组中,兵重新画图,如果文字多行,要考虑换行,如果高度超过矩形的高度,要出现滚动条)

(图六:发送界面,把手机号码涂改了一下,点发送菜单就可以发短信了。)

完毕,继续看M$ COM了.

代码:
//这个代码是来自FormCanvas的扩展的。

import javax.microedition.io.Connector;
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.TextBox;
import javax.microedition.lcdui.TextField;
import javax.wireless.messaging.MessageConnection;
import javax.wireless.messaging.TextMessage;
 
import com.zhanglihai.j2me.event.IKeyListener;
import com.zhanglihai.j2me.event.IMenuItemListener;
import com.zhanglihai.j2me.ui.Component;
import com.zhanglihai.j2me.ui.Constants;
import com.zhanglihai.j2me.ui.FormCanvas;
import com.zhanglihai.j2me.ui.Header;
import com.zhanglihai.j2me.ui.KEvent;
import com.zhanglihai.j2me.ui.Menu;
import com.zhanglihai.j2me.ui.MenuBar;
import com.zhanglihai.j2me.ui.MenuItem;
import com.zhanglihai.j2me.ui.MenuItemEvent;
 
/**
*
* @project : j2me-framework
* @author : zhanglihai
* @home : <a href="http://www.zhanglihai.com/">http://www.zhanglihai.com</a>
* @email : <a href="mailto:[email protected]">[email protected]</a>
* @date : 2007-9-7
* @file : SmsSendForm.java
* @version : 1.0
*
* $Id: SmsSendForm.java,v 1.0 2007/07/07 14:51:11 zhanglihai Exp $
*/
public class SmsSendForm extends FormCanvas implements CommandListener,
IKeyListener, IMenuItemListener {
 
private TestJ2MEFramework parent;
 
private TextBox mobileBox;
 
private Command cmdOk = new Command("完成", Command.OK, 1);
 
private Command cmdBack = new Command("后退", Command.BACK, 1);
 
private MenuBar menuBar = null;
 
private Header header = null;
 
private Component label = null;
 
private Menu mainMenu;
 
private MenuItem itemSend;
 
private MenuItem itemContent;
 
private int currentField = -1;
 
private TextBox contentBox = null;
 
private String[] m_strValues = new String[2];;
 
private TextBox[] m_textBoxs = new TextBox[2];
 
public SmsSendForm(final TestJ2MEFramework parent) {
this.parent = parent;
this.mobileBox = new TextBox("手机号码", "", 11, TextField.PHONENUMBER);
this.mobileBox.addCommand(cmdOk);
this.mobileBox.addCommand(cmdBack);
mobileBox.setCommandListener(this);
this.contentBox = new TextBox("内容", "", 70, TextField.ANY);
contentBox.addCommand(cmdOk);
contentBox.addCommand(cmdBack);
contentBox.setCommandListener(this);
m_textBoxs[0] = mobileBox;
m_textBoxs[1] = contentBox;
// /custom..
this.menuBar = new MenuBar();
this.header = new Header("发送短信");
this.label = new Component("后退");
label.setKeyListener(new IKeyListener() {
public void actionPerformed(Object obj, KEvent e) {
parent.setHome();
}
});
this.mainMenu = new Menu("菜单");
this.itemSend = new MenuItem("发送");
this.itemContent = new MenuItem("内容");
mainMenu.addItem(itemSend);
mainMenu.addSeparator();
mainMenu.addItem(itemContent);
mainMenu.setItemListener(this);
this.menuBar.setHeight(30);
this.menuBar.setLabel(label);
this.menuBar.setMenu(mainMenu);
setHeader(header);
setMenuBar(menuBar);
setKeyPressedListener(this);
setBackground(0xFFFFFF);
// /custom
}
 
protected void drawWorkspace(Graphics g) {
Font font = Constants.PLAIN_MEDIUM_FONT;
g.setFont(font);
g.setColor(0);
String str = "手机号码";
int offx = 5;
int x = offx;
int fh = font.getHeight();
g.drawString(str, 5, 5 + (30 - fh) / 2, Graphics.LEFT | Graphics.TOP);
if (currentField == 0)
g.setColor(0x336699);
else
g.setColor(0xCCCCCC);
x = offx + font.stringWidth(str) + 2;
g.fillRect(x, 5, workspaceWidth - x - offx, 30);
g.setColor(0xFFFFFF);
g.fillRect(x + 2, 5 + 2, workspaceWidth - x - offx - 4, 30 - 4);
char[] ch_str;
int p_str_w = 0;
int p_str_index = 0;
if (m_strValues[0] != null) {
g.setColor(0);
ch_str = m_strValues[0].toCharArray();
for (; p_str_index &lt; ch_str.length; p_str_index++) {
p_str_w += font.charWidth(ch_str[p_str_index]);
if (p_str_w &gt;= (workspaceWidth - x - offx - 4))
break;
}
g.drawChars(ch_str, 0, p_str_index, x + 2, 5 + 2, Graphics.LEFT
| Graphics.TOP);
}
str = "短信内容";
g.setColor(0);
g.drawString(str, offx, 40, Graphics.LEFT | Graphics.TOP);
if (currentField == 1)
g.setColor(0x336699);
else
g.setColor(0xCCCCCC);
g.fillRect(offx, 40 + fh + 5, workspaceWidth - 10, workspaceHeight - 45
- 2 - fh);
g.setColor(0xFFFFFF);
g.fillRect(offx + 2, 40 + fh + 5 + 2, workspaceWidth - 10 - 4,
workspaceHeight - 45 - 2 - 4 - fh);
int c_w = font.charWidth('中');
if (m_strValues[1] != null) {
g.setColor(0);
// 要计算一行显示几个字,到哪个字的时候回行
ch_str = m_strValues[1].toCharArray();
p_str_w = p_str_index = 0;
int p_line = 0;
x = 0;
int y = 0;
for (; p_str_index &lt; ch_str.length; p_str_index++) {
if (p_str_w &gt;= (workspaceWidth - 10 - 4 - c_w)) {
p_line++;
p_str_w = 0;
}
 
x = offx + 2 + p_str_w;
y = 40 + fh + 5 + 2 + p_line * fh;
// 没有实现滚动条啊
if (y &gt;= workspaceHeight - 45 - 2 - 4 - fh)
break;
g.drawChar(ch_str[p_str_index], x, y, Graphics.LEFT
| Graphics.TOP);
p_str_w += font.charWidth(ch_str[p_str_index]);
}
}
}
 
public void actionPerformed(Object obj, KEvent e) {
switch (e.gameAction) {
case UP:
currentField--;
if (currentField &lt; 0)
currentField = 0;
repaint();
break;
case DOWN:
currentField++;
if (currentField &gt; 1)
currentField = 1;
repaint();
break;
default:
if (e.keyCode == CENTER_OK_KEY) {
parent.display.setCurrent(m_textBoxs[currentField]);
}
break;
 
}
}
 
public void commandAction(Command c, Displayable d) {
if (c == cmdOk) {
this.m_strValues[currentField] = m_textBoxs[currentField]
.getString();
parent.display.setCurrent(this);
} else
parent.display.setCurrent(d);
 
}
 
private Alert alert = new Alert("信息", "", null, AlertType.INFO);
 
private void sendSms() {
if (m_strValues == null || m_strValues[0] == null
|| m_strValues[0].trim().length() != 11) {
alert.setType(AlertType.ERROR);
alert.setString("没有写信人号码啊");
parent.display.setCurrent(alert);
}
Thread thread = new Thread(new Runnable() {
 
public void run() {
 
String address = "sms://" + m_strValues[0];
MessageConnection mesconn = null;
try {
mesconn = (MessageConnection) Connector.open(address);
 
TextMessage textmessage = (TextMessage) mesconn
.newMessage(MessageConnection.TEXT_MESSAGE);
textmessage.setAddress(address);
textmessage.setPayloadText(m_strValues[1]);
mesconn.send(textmessage);
alert.setType(AlertType.INFO);
alert.setString("发送成功");
} catch (Throwable t) {
t.printStackTrace();
alert.setType(AlertType.ERROR);
alert.setString("发送失败");
} finally {
try {
mesconn.close();
} catch (Exception ioe) {
}
}
parent.display.setCurrent(alert);
}
});
 
thread.start();
 
}
 
public void actionPerformed(MenuItemEvent e) {
if (e.item == this.itemContent) {
this.currentField = 1;
parent.display.setCurrent(contentBox);
} else {
sendSms();
}
 
}
 
}

J2ME自己画界面之–日历的实现

今天又有空闲时间了,接着来做日历控件。

其实日历控件有些拿不出手,因为这个太单了啊,用各种语言都可以做,最常见的就是web界面上的JS实现的了。不管怎么说,还是做了一个玩玩吧。

程序不用多说了,还是继承自自己定义的Form,日历程序没有什么好说的,不能再简单了。

看看截图吧。

(图一:显示农历的日历用四个方向键来改变日期)

(图二:去掉显示农历)

(图三:显示结果)

有意思吧。。。。。

下次搞点啥呢?