作者:Eric Giguere
Dec.20, 2001
MIDP高层用户界面API定义了几个组件,用于在Form对象中使用。这些组件被作为“项目”,因为它们是javax.microedition.lcdui.Item类的扩展。一些Item是可以被用户修改的,包括ChoiceGroup,Gauge,DateField和TextField项目。
有时,应用需要知道什么时候可编辑项目的值或选项发生变化。例如,应用可能希望更新一个gauge的基于Label的当前值。或者应用希望确认test域中的输入数值。这时ItemStateListener接口是非常重要的。这个接口很简单,是由一些简单的方法组成的:
package javax.microedition.lcdui.*;
public interface ItemStateListener {
void itemStateChanged(Item item);
}
如果你想知道项目状态的变化,只需的注册一个对象来实现ItemStateListener接口:
Form f = new Form(); ItemStateListener listener = ....; // form.setItemStateListener( listener );
无论何时一个可编辑项目由于用户的交互而改变了状态,它调用ItemStateChanged方法。状态改变的项目只是作为一个参数被传递。注意:这种提示是在项目的状态变化以后发生的—如果你需要了解旧的状态,你必须另外跟踪它。
让我们来使用ItemStateListener实现一些数值输入的确认。像我们在2001年11月14日的Tip中讨论的“Developing
Custom Components for the Mobile Information Device Profile”,一个TextField组件中可以接受的类型能够被简单的限制。一个TextField可以通过设置TextField.NUMERIC约束来限制它只接收数值输入。不幸的是,对于这种简单的约束没有检验。例如,假设你的应用希望用户输入一个电话号码。尽管TextField类支持一个PHONENUMBER约束,但是它的功能太有限或不能满足你的想法。使用ItemStateListener,你可以创建一个电话号码输入Form,使它只接收特定类型的电话号码。实际上,在这个Tip中,你将看到一个更通用的Form,成文NumericInputForm,可以被用于数字数值输入,而不仅仅是电话号码。
一个NumericInputForm上有两个Item:一个标题为TextField和另一个StringItem。TextField是用户输入电话号码的。它使用了NUMERIC约束――换句话说,用户输入一个非格式化的数字。(这在MIDP设备上很容易使用,因为它意味着用户可以直接在键盘上输入数字,而无须象通常的文本输入那样,在字符和符号之间进行切换。)
StringItem显示数字格式化的形式。Form有两个命令对象与它关联:一个是“Done”命令和一个“Cancel”命令。“Cancel”命令总是可用的,但是“Done”命令只有在用户的输入与某个输入掩码匹配是才有效。下面是NumericInputForm的代码:
import javax.microedition.lcdui.*;
// Defines a form that prompts the user to
// enter a numeric value and checks it against
// a set of input masks. If the number matches
// a mask, the "Done" command is made visible
// and the user can then proceed to the next
// form.
public class NumericInputForm extends Form implements ItemStateListener {
public static final Command DONE_COMMAND =
new Command("Done", Command.OK, 1);
public static final Command CANCEL_COMMAND =
new Command(
"Cancel", Command.CANCEL, 1);
private String[] masks;
private int[] digits;
private Command done;
private Command cancel;
private TextField field;
private StringItem match;
public NumericInputForm(String title,
String label,
String[] masks,
String value) {
this(title, label, masks, value,
null, null);
}
public NumericInputForm(String title,
String label,
String[] masks,
String value,
Command done,
Command cancel) {
super(title);
this.masks = masks;
this.done = (done != null ? done
: DONE_COMMAND);
this.cancel = (cancel != null ? cancel
: CANCEL_COMMAND);
digits = new int[masks.length];
int maxlen = 0;
for (int i = 0; i < masks.length; ++i) {
int mlen = countDigitsInMask(masks[i]);
if (mlen > maxlen) {
maxlen = mlen;
}
digits[i] = mlen;
}
field = new TextField(label, value,
maxlen,
TextField.PHONENUMBER);
append(field);
match = new StringItem(null, "");
append(match);
adjustState();
addCommand(this.cancel);
setItemStateListener(this);
}
// Adjust the state of the form based on changes
// made to the input field. Adds or removes the
// "done" command as appropriate, and also adjusts
// the value of the label showing the masked
// input that matches, if any.
protected void adjustState() {
String val = field.getString();
String applied = null;
for (int i = 0; i < masks.length; ++i) {
applied = matches(
val, digits[i], masks[i]);
if (applied != null) {
break;
}
}
if (applied != null) {
match.setText(applied);
addCommand(done);
} else {
match.setText("");
removeCommand(done);
}
}
// Figure out how many digits need to be entered
// for a particular mask.
private int countDigitsInMask(String mask) {
int count = 0;
for (int i = 0; i < mask.length(); ++i) {
char ch = mask.charAt(i);
if (ch == '#' || Character.isDigit(ch)) {
++count;
}
}
return count;
}
// Return the masked value.
public String getMaskedValue() {
return match.getText();
}
// Return the raw value (numbers only).
public String getRawValue() {
return field.getString();
}
// Our callback, just calls adjustState.
public void itemStateChanged(Item item) {
adjustState();
}
// Check to see if the given string matches
// the given mask.
protected String matches(String value,
int digits,
String mask) {
if (value.equals(mask)) {
return value;
}
int vlen = value.length();
if (vlen != digits) {
return null;
}
int mlen = mask.length();
int vindex = 0;
StringBuffer b = new StringBuffer(mlen);
for (int i = 0; i < mlen; ++i) {
char mch = mask.charAt(i);
char vch = mch;
if (mch == '#') {
vch = value.charAt(vindex++);
} else if (Character.isDigit(mch)) {
vch = value.charAt(vindex++);
if (mch != vch) {
return null;
}
}
b.append(vch);
}
return b.toString();
}
// Adjust the field's value.
public void setRawValue(String value) {
field.setString(value);
adjustState();
}
}
注意:构造函数带有一个字符串数组。这些字符串是Form接收的数据的掩码。使用“#”字符来指示一个数字的位置。例如,接收北美类型的电话号码,掩码为:
String[] masks = new String[] {"###-####",
"(###) ###-####"};
下面是简单的MIDlet使用NumericInputForm类来引导用户输入电话号码。
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
import javax.microedition.rms.*;
// A simple test of the NumericInputForm class
// that prompts the user to enter a phone number
// in one of two acceptable formats.
public class NumericInputTest extends MIDlet {
private Display display;
private Command exitCommand =
new Command("Exit",
Command.EXIT, 1);
private Command okCommand =
new Command("OK",
Command.OK, 1);
public NumericInputTest() {
}
protected void destroyApp(boolean unconditional) throws
MIDletStateChangeException {
exitMIDlet();
}
protected void pauseApp() {
}
protected void startApp() throws MIDletStateChangeException {
if (display == null) {
initMIDlet();
}
}
private void initMIDlet() {
display = Display.getDisplay(this);
display.setCurrent(new PromptForPhone());
}
public void exitMIDlet() {
notifyDestroyed();
}
private String[] phoneMasks =
new String[] {"###-####",
"(###) ###-####"};
// A subclass of NumericInputForm that does nothing
// but set the masks and the initial values and
// registers itself as a command listener.
class PromptForPhone extends NumericInputForm implements CommandListener {
public PromptForPhone() {
super("Test", "Enter a phone number:",
phoneMasks, "");
setCommandListener(this);
}
// Called when the user presses either the
// "done" or "cancel" commands on the
// numeric input form.
public void commandAction(Command c,
Displayable d) {
if (c == CANCEL_COMMAND) {
exitMIDlet();
} else {
Alert a = new Alert("Result");
a.setString("You entered the number " +
getMaskedValue() +
" from the string " +
getRawValue());
a.setTimeout(Alert.FOREVER);
display.setCurrent(a, this);
setRawValue("");
}
}
}
}
对NumericInputForm类可以有多种改进。例如,它可以允许部分匹配,或它可以动态调整后台的TextField的约束,是用户输入不仅仅是数字字符。