在应用程序开发中经常遇到用户输入的问题;Sun MIDP提供了TextField控件;这个控件用起来不方便,一个原因是TextField的实现,不同的java虚拟机移植,实现不同,有的java虚拟机实现不方便,应用开发者对此无能为力;比如我现在用的这个j2me平台,用户输入必须要先暂停虚拟机,切换到另一个本地实现的窗口,输入完毕后,再恢复虚拟机;客户对此抱怨很大;另一个原因是高级UI入门简单,但要实现自己想要的功能,就显得不灵活,而且经常会添乱。所以,我决定写一个用户输入类,支持英文字符输入和数字输入,支持输入模式切换,不支持汉字输入,不需要暂停虚拟机。而LWUIT的TextField控件恰好实现了我想要的功能。
简单地模仿它写了一段测试代码:
import javax.microedition.lcdui.*;
import java.util.Hashtable;
import java.util.Vector;
public class inputCanvas extends Canvas{
String m_strInput = "";
private static Hashtable inputModes;
private static Vector v_InputMode = new Vector();
private static Object s_CurrInputMode;
private static final String[] DEFAULT_KEY_CODES = {
// 0
" 0",
// 1
".,?!'/"1-()@/:_",
// 2
"ABC2",
// 3
"DEF3",
// 4
"GHI4",
// 5
"JKL5",
// 6
"MNO6",
// 7
"PQRS7",
// 8
"TUV8",
// 9
"WXYZ9",
};
private int m_iPressCount = 0;
private int m_iPrevKeyCode = 0;
private final static int fs_maxLength = 10;
static{
initInputModes();
}
public inputCanvas(){
this.setFullScreenMode(true);
}
public void paint(Graphics g){
g.setColor(0xFFFFFF);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(0x000000);
g.drawString(m_strInput, 0, 0, Graphics.LEFT|Graphics.TOP);
g.drawString((String)s_CurrInputMode, 0, 50, Graphics.LEFT|Graphics.TOP);
}
protected void keyPressed(int keyCode){
m_iPressCount = 0;
m_iPrevKeyCode = keyCode;
if((keyCode >= 48)&&(keyCode <= 57)){
char c = getCharPerKeyCode(m_iPressCount,(keyCode-48));
insertChars(""+c,false);
}else if(keyCode == 35){
switchInputModes();
}else if(keyCode == -7){
deleteChar();
}
repaint();
}
protected void keyRepeated(int keyCode){
if((keyCode >= 48)&&(keyCode <= 57)&&(m_iPrevKeyCode == keyCode)){
m_iPressCount++;
char c = getCharPerKeyCode(m_iPressCount,(keyCode-48));
insertChars(""+c,true);
repaint();
}
}
protected void deleteChar(){
if(m_strInput.length() > 0){
m_strInput = m_strInput.substring(0, m_strInput.length()-1);
}
}
protected void insertChars(String c, boolean bOverWrite){
if(c == null){
return;
}
if(c.length() != 1){
return;
}
if(bOverWrite){
m_strInput = m_strInput.substring(0, m_strInput.length()-1)+c;
}else{
if(m_strInput.length() < fs_maxLength){
m_strInput = m_strInput + c;
}
}
}
protected char getCharPerKeyCode(int pressCount, int keyCode){
Hashtable mode = (Hashtable)inputModes.get(s_CurrInputMode);
String str = (String)mode.get(new Integer(keyCode+'0'));
if(str != null){
pressCount = pressCount%str.length();
return str.charAt(pressCount);
}
return 0;
}
private void switchInputModes(){
int iIndex = v_InputMode.indexOf(s_CurrInputMode);
iIndex = (iIndex+1)%v_InputMode.size();
s_CurrInputMode = v_InputMode.elementAt(iIndex);
}
private static void initInputModes(){
if(inputModes == null) {
inputModes = new Hashtable();
Hashtable upcase = new Hashtable();
for(int iter = 0 ; iter < DEFAULT_KEY_CODES.length ; iter++) {
upcase.put(new Integer('0' + iter), DEFAULT_KEY_CODES[iter]);
}
v_InputMode.addElement("ABC");
inputModes.put("ABC", upcase);
Hashtable lowcase = new Hashtable();
for(int iter = 0 ; iter < DEFAULT_KEY_CODES.length ; iter++) {
lowcase.put(new Integer('0' + iter), DEFAULT_KEY_CODES[iter].toLowerCase());
}
v_InputMode.addElement("abc");
inputModes.put("abc", lowcase);
Hashtable numbers = new Hashtable();
for(int iter = 0 ; iter < 10 ; iter++) {
numbers.put(new Integer('0' + iter), "" + iter);
}
inputModes.put("123", numbers);
v_InputMode.addElement("123");
s_CurrInputMode = v_InputMode.elementAt(0);
}
}
}
基本的原理是:
1. keyPress和keyRepeated;通过特殊的按键切换输入模式、删除键值;我这里没有做光标;
2. 一种输入模式对应一个Hashtable,然后将所有的输入模式放在一个hashtable;根据输入模式、键值和按键次数查询字符;
实际的效果并不好;经过测试发现,当一直按住某个键时,首先是keyPressed被回调,然后是keyRepeated不断地被调用;要切换某个字符,唯一的选择是一直按住某个键,直到想要的那个字符出现;而实际的情况是,按键的响应快过显示结果,很难精确地在看到某个字符时释放按键。
还需要改进。