作者:stone111 文章来源:http://www.j2medev.com/Article/ShowArticle.asp?ArticleID=276
3.1 概述我们在这一节要介绍一下整个LCDUI包的结构,让读者对我们整个UI的学习的有个大致的了解。下图为我们展示了整个LCDUI包的体系:
Screen类属于高级图形用户界面组件,就是我们这一章要着重介绍的内容,Canvas是低级图形用户界面组件,在同一时刻,只能有唯一一个Screen或者Canvas类的子类显示在屏幕上,我们可以调用Display的setCurrent()的方法来将前一个画面替换掉,我们必须自行将前一个画面的状态保留起来,并自己控制整个程序画面的切换。
同时我们可以运用javax.microedition.lcdui.Command类来给我们的提供菜单项目的功能,分别是:Command.BACKCommand、Command.CANCEL、Command.EXIT、Command.HELP、Command.ITEM、Command.OK、Command.SCREEN和Command.STOP,我们在Displayable对象中定义了addCommand()和removeCommand()两个方法,这就意味着我们可以在高级UI和低级UI中同时使用Command类,同时我们通过注册Command事件来达到事件处理的目的,即Command必须与CommandListener接口配合使用才能反映用户的动作,具体的使用方法我们在具体的示例中会给出详细的用法,读者可以参阅API的说明文档获得进一步的认识。
还有在Displayable类的子类中都加入了Ticker,我们可以用setTicker()来设定画面上的Ticker,或者用getTicker()这个方法来取得画面所含的Ticker对象。
下面我们给出Screen类的主要结构图:
3.2 列表List根据第零节的概述我们已经大概了解了Lcdui这个包,现在让我们来开始介绍Screen这个类里面的几个重要的类,我们本节介绍的是Screen的一个子类List,它一共有三种具体的类型:implicit(简易式),exclusive(单选式),multiple(多选式)。
与相关的List元素相关的应用程序操作一般可概括为ITEM型命令(在后续章节将会有详细介绍)或者SCREEN类型命令,其作用域范围的判断依据是看该操作是影响到被选择原则元素还是整个List来判定,List对象上的操作包括insert,append和delete,用于约束List具体类型的类是ChoiceGroup,List中的元素可以用getString、insert、set、append、delete、getImage等方法来具体操纵,对于项目的选择我们则使用getSelectedIndex()、setSelectedIndex()、getSelectedFlags()、setSelectedFlags()和isSelected()来处理,下面我们来详细介绍一下第一段提到的三个List类型。
3.2.1 Exclusive(单选式)和所有的List一样,我们可以在构造函数中指定它的标题和类型(构造函数类型1),也可以使用另一种构造函数类型,即直接传入一个String数组和一个Image数组,这种构造函数可以直接对List内容进行初试化(构造函数类型2),在我们进行的大多数开发中,类型1的使用是比较常见的,读者可以通过阅读API说明文档对其进行深入的掌握。
在类型1当中,我们需要对其增加内容的时候,就需要用到前面提到的append()方法了,该构造函数的第一个参数是屏幕上的文字,第二个则是代表选项的图标,当不需要图标的时候,和我们大多数的处理方法相同,只需传入NULL这个参数就行了,任何时候我们可以用insert()方法来插入项目,用set()方法来来重新设置一个项目,当我们不需要一个项目的时候,可以用delete()方法来删除特定的选项,我们只需往该方法内传入索引值即可,需要注意的是我们的索引值是从0开始,deleteAll()这个方法则是一次性删除所有的指定List的内容。
我们在命令处理函数commandAction()中,可以用上面提到的几种方法来对用户选择的操作进行侦测,同时定义好对应的处理函数,来达到对应的处理效果。
3.2.2 Implicit (隐含式)IMPLICIT(隐含式)其实和上面的单选式没什么大的区别,唯一不同的地方在于命令的处理机制上有一些细微的区别:Choice.IMPLICIT类型的List会在用户选择之后立刻引发事件,并将List.SELECTCOMMAND作为第一个参数传入。
如果我们不希望该类型的List在按下后发出该命令作为commandAction ()的第一个参数传入,我们可以用setSelectCommand(null),将它关掉,需要注意的是,这样做的后果是使commandAction()接受到的第一个参数为null。
3.2.3 Multiple(多选式)multiple(多选式)类型的List顾名思义,可以进行多重选择,其他的地方和上面两种类型大同小异,可以进行多项的List选择。
下面我们以WTK2.1自带的DEMO为例,通过一段代码来加深巩固我们这一小节的内容:
public class ListDemoextends MIDlet implements CommandListener
{
//这里注意如何使用
//CommandListener这个接口
private final static Command CMD_EXIT = new Command("Exit", Command.EXIT, 1);
private final static Command CMD_BACK = new Command("Back", Command.BACK, 1);
private Display display;
private List mainList;
private List exclusiveList;
private List implicitList;
private List multipleList;
private boolean firstTime;
public ListDemo() {
display = Display.getDisplay(this);
String[] stringArray = {
"Option A",
"Option B",
"Option C",
"Option D"
};
//待传入进行初始化的String数组,即Choice选项的文字部分。
Image[] imageArray = null;
//我们这里只是为Image[]数组进行初始化。
exclusiveList = new List("Exclusive", Choice.EXCLUSIVE, stringArray,
imageArray);
exclusiveList.addCommand(CMD_BACK);
exclusiveList.addCommand(CMD_EXIT);
exclusiveList.setCommandListener(this);
//ExlcusiveList的声明
implicitList = new List("Implicit", Choice.IMPLICIT, stringArray,
imageArray);
implicitList.addCommand(CMD_BACK);
implicitList.addCommand(CMD_EXIT);
implicitList.setCommandListener(this);
//ImplicitList的声明
multipleList = new List("Multiple", Choice.MULTIPLE, stringArray,
imageArray);
multipleList.addCommand(CMD_BACK);
multipleList.addCommand(CMD_EXIT);
multipleList.setCommandListener(this);
//MutipleList的声明
firstTime = true;
}
protected void startApp() {
if(firstTime)
Image[] imageArray = null;
try
{
Image icon = Image.createImage("/midp/uidemo/Icon.png");
//注意!这里的路径是相对路径,请大家千万注意这里的细节问题
imageArray = new Image[] {
icon,
icon,
icon
};
} catch (java.io.IOException err) {
// ignore the image loading failure the application can recover.
}
String[] stringArray = {
"Exclusive",
"Implicit",
"Multiple"
};
mainList = new List("Choose type", Choice.IMPLICIT, stringArray,
imageArray);
mainList.addCommand(CMD_EXIT);
mainList.setCommandListener(this);
display.setCurrent(mainList);
firstTime = false;
}
}
protected void destroyApp(boolean unconditional) {
}
protected void pauseApp() {
}
public void commandAction(Command c, Displayable d) {
//注意这里是如何实现CommandListener这个接口的!
if (d.equals(mainList)) {
if (c == List.SELECT_COMMAND) {
if (d.equals(mainList)) {
switch (((List)d).getSelectedIndex()) {
case 0:
display.setCurrent(exclusiveList);
break;
case 1:
display.setCurrent(implicitList);
break;
case 2:
display.setCurrent(multipleList);
break;
}
}
}
} else {
// in one of the sub-lists
if (c == CMD_BACK) {
display.setCurrent(mainList);
}
}
if (c == CMD_EXIT) {
destroyApp(false);
notifyDestroyed();
}
}
}
3.3 TextBox当我们要在移动设备上输入数据时,TextBox就派上用场了,我们可以TextBox的构造函数参数共有四个,第一个是我们长说的Title,即标题,第二个是TextBox的初始内容,第三个是允许输入字符的最大长度,第四个是限制类型,关于限制类型我们一般按照限制存储内容和限制系统的类型分为两种,这两种各有6个具体的类型,大家可以参阅API说明文档,获得具体类型的运用,在这里我想要提醒读者注意的一点是:一个TextBox必须附加一个命令,否则,用户将不能激发任何行为,而陷入这个TextBox中。
我们给出一个常见的TextBox的例子,让大家进一步了解一下TextBox:
import javax.microedition.lcdui.*;
import javax.microedition.midlet.MIDlet;
public class TextBoxDemo
extends MIDlet
implements CommandListener {
private Display display;
private ChoiceGroup types;
private ChoiceGroup options;
private Form mainForm;
private final static Command CMD_EXIT = new Command("Exit",
Command.EXIT, 1);
private final static Command CMD_BACK = new Command("Back",
Command.BACK, 1);
private final static Command CMD_SHOW = new Command("Show", Command.SCREEN,
1);
/** TextBox的labels
*
*/
static final String[] textBoxLabels = {
"Any Character", "E-Mail", "Number", "Decimal", "Phone", "Url"
};
/**
* 这里列出了几种TextBox的Types
*/
static final int[] textBoxTypes = {
TextField.ANY, TextField.EMAILADDR, TextField.NUMERIC,
TextField.DECIMAL, TextField.PHONENUMBER, TextField.URL
};
private boolean firstTime;
public TextBoxDemo() {
display = Display.getDisplay(this);
firstTime = true;
}
protected void startApp() {
if(firstTime) {
mainForm = new Form("Select a Text Box Type");
mainForm.append("Select a text box type");
// the string elements will have no images
Image[] imageArray = null;
types = new ChoiceGroup("Choose type", Choice.EXCLUSIVE,
textBoxLabels, imageArray);
mainForm.append(types);
// 进一步选择的选项
String[] optionStrings = { "As Password", "Show Ticker" };
options = new ChoiceGroup("Options", Choice.MULTIPLE,
optionStrings, null);
mainForm.append(options);
mainForm.addCommand(CMD_SHOW);
mainForm.addCommand(CMD_EXIT);
mainForm.setCommandListener(this);
firstTime =false;
}
display.setCurrent(mainForm);
}
protected void destroyApp(boolean unconditional) { /*抛出异常throws MIDletStateChangeException*/
}
protected void pauseApp() {
}
public void commandAction(Command c, Displayable d) {
if (c == CMD_EXIT) {
destroyApp(false);
notifyDestroyed();
} else if (c == CMD_SHOW) {
// these are the images and strings for the choices.
Image[] imageArray = null;
int index = types.getSelectedIndex();
String title = textBoxLabels[index];
int choiceType = textBoxTypes[index];
boolean[] flags = new boolean[2];
options.getSelectedFlags(flags);
if (flags[0]) {
choiceType |= TextField.PASSWORD;
}
TextBox textBox = new TextBox(title, "", 50,
choiceType);
if (flags[1]) {
textBox.setTicker(new Ticker("TextBox: " + title));
}
textBox.addCommand(CMD_BACK);
textBox.setCommandListener(this);
display.setCurrent(textBox);
} else if (c == CMD_BACK) {
display.setCurrent(mainForm);
}
}
}
3.4 Alert 这个类比较有意思,它是用来提醒用户关于错误或者其他异常情况的屏幕对象,这个警告只能作为简短的信息记录和提醒,如果我们需要长一点的,我们可以使用其它的Screen子类,最常见的是Form。同时我们顺便提一下跟它相关的一个类AlertType,需要提醒读者注意的一点是AlertType是一个本身无法实体化的工具类。(即我们不能象Form那样产生具体的对象)
AlertType共有5个类型:ALARM(警报),CONFIRMATION(确定),ERROR(错误),INFO(信息提示),WARNING(警告)。
Alert是一个比较特殊的屏幕对象,当我们在setCurrent()方法中调用它的时候,它会先发出一段警告的声音,然后彩绘显示在屏幕上,过了一段时间之后,它会自动跳回之前的画面。
我们需要注意的是我们必须在使用setCurrent()显示Alert之前定义好它可以跳回的画面,否则会发生异常。
在Alert中我们可以通过setTimeout()方法来设定间隔的时间,setType()来调用我们上面提到的四种类型,setImage()来定义图片,setString()来定义内含文字,同时通过getType(),getImage(),getString()来取得相应的对象。
当Alert显示了我们在setTimeout()中指定的间隔时间后,它会跳回我们之前指定的对象,如果我们在指定显示时间时传入了Alert.FOREVER作为参数,这时,除非用户按下定义哈哦的接触键,否则,屏幕会一直显示这个Alert。如果在一个定时的Alert中只有一个命令,那么超时发生时命令会自动激活。
import javax.microedition.lcdui.*;
import javax.microedition.midlet.MIDlet;
public class AlertDemo
extends MIDlet {
private final static Command CMD_EXIT = new Command("Exit", Command.EXIT,
1);
private final static Command CMD_SHOW = new Command("Show", Command.SCREEN,
1);
private final static String[] typeStrings = {
"Alarm", "Confirmation", "Error", "Info", "Warning"
};
private final static String[] timeoutStrings = {
"2 Seconds", "4 Seconds", "8 Seconds", "Forever"
};
private final static int SECOND = 1000;
private Display display;
private boolean firstTime;
private Form mainForm;
public AlertDemo() {
firstTime = true;
mainForm = new Form("Alert Options");
}
protected void startApp() {
display = Display.getDisplay(this);
showOption();
}
/**
* 制造这个MIDlet的基本显示
* 在这个Form里面我们可以选择Alert的个中类型和特性
*/
private void showOption() {
if(firstTime) {
// choice-group for the type of the alert:
// "Alarm", "Confirmation", "Error", "Info" or "Warning"
ChoiceGroup types = new ChoiceGroup("Type", ChoiceGroup.POPUP,
typeStrings, null);
mainForm.append(types);
// choice-group for the timeout of the alert:
// "2 Seconds", "4 Seconds", "8 Seconds" or "Forever"
ChoiceGroup timeouts = new ChoiceGroup("Timeout", ChoiceGroup.POPUP,
timeoutStrings, null);
mainForm.append(timeouts);
// a check-box to add an indicator to the alert
String[] optionStrings = { "Show Indicator" };
ChoiceGroup options = new ChoiceGroup("Options", Choice.MULTIPLE,
optionStrings, null);
mainForm.append(options);
mainForm.addCommand(CMD_SHOW);
mainForm.addCommand(CMD_EXIT);
mainForm.setCommandListener(new AlerListener(types, timeouts, options));
firstTime = false;
}
display.setCurrent(mainForm);
}
private class AlerListener
implements CommandListener {
AlertType[] alertTypes = {
AlertType.ALARM, AlertType.CONFIRMATION, AlertType.ERROR,
AlertType.INFO, AlertType.WARNING
};
ChoiceGroup typesCG;
int[] timeouts = { 2 * SECOND, 4 * SECOND, 8 * SECOND, Alert.FOREVER };
ChoiceGroup timeoutsCG;
ChoiceGroup indicatorCG;
public AlerListener(ChoiceGroup types, ChoiceGroup timeouts,
ChoiceGroup indicator) {
typesCG = types;
timeoutsCG = timeouts;
indicatorCG = indicator;
}
public void commandAction(Command c, Displayable d) {
if (c == CMD_SHOW) {
int typeIndex = typesCG.getSelectedIndex();
Alert alert = new Alert("Alert");
alert.setType(alertTypes[typeIndex]);
int timeoutIndex = timeoutsCG.getSelectedIndex();
alert.setTimeout(timeouts[timeoutIndex]);
alert.setString(
typeStrings[typeIndex] + " Alert, Running " + timeoutStrings[timeoutIndex]);
boolean[] SelectedFlags = new boolean[1];
indicatorCG.getSelectedFlags(SelectedFlags);
if (SelectedFlags[0]) {
Gauge indicator = createIndicator(timeouts[timeoutIndex]);
alert.setIndicator(indicator);
}
display.setCurrent(alert);
} else if (c == CMD_EXIT) {
destroyApp(false);
notifyDestroyed();
}
}
}
protected void destroyApp(boolean unconditional) {
}
protected void pauseApp() {
}
/**
* 我们在这里生成Alert的indicator.
* 如果这里没有timeout, 那么这个indicator 将是一个 "非交互性的" gauge
* 用有一个后台运行的thread更新.
*/
private Gauge createIndicator(int maxValue) {
if (maxValue == Alert.FOREVER) {
return new Gauge(null, false, Gauge.INDEFINITE,
Gauge.CONTINUOUS_RUNNING);
}
final int max = maxValue / SECOND;
final Gauge indicator = new Gauge(null, false, max, 0);
// if (maxValue != Gauge.INDEFINITE) {
new Thread() {
public void run() {
int value = 0;
while (value < max) {
indicator.setValue(value);
++value;
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
// ignore
}
}
}
}.start();
}
return indicator;
}
}
3.5 Form概述Form是J2ME里面一个比较重要的容器类型,可以说是集中了高级UI中的精华,是开发当中常常用到的一个关键类,下图很好的说明了FORM及其相关子类的关系:
我们通常是往Form里面添加个中Item的子类(使用append()方法),从而达到让画面更加丰富的目的,每一个Item的子类在同一时刻只能属于同一个容器,否则会引发异常。
在Form画面中,我们通过Item.LAYOUT_LEFT、Item.LAYOUT_CENTER和Item.LAYOUT_RIGHT来控制各个Item在Form的位置,通过这几个参数的字面意思我们很容易明白分别是左,中,右。在不设定的情况下,Item会依照LAYOUT_DEFAULT来绘制,如果我们希望自己来设定等效线,可以用setLayout()这个方法来控制。
同时,Form缺省的设定会在空间足够的情况下,尽可能让Item出现在同一个逻辑区域中。如果组件在显示时,比我们预期的最大的尺寸要大(或比预期最小尺寸更小),那么系统会自动忽略我们之前的设定,转而采用最大尺寸或者最小尺寸,这时系统会自动调用setPreferredSize(),将预期尺寸设置好。
3.6 StringItem及ImageItem3.6.1 StringItemStringItem的作用,从它字面上意思来看就可以很明白,就是在屏幕上显示一串字,配合不同的外观类型, StringItem有两个构造函数,最长见的是需要三个参数的,第一个是Label,第二个是内容,第三个则是外观,外观共分三种:PLAIN,BUTTON,HYPERLINK,(只需两个参数的构造函数等同于使用PLAIN的外观的三个参数的构造函数),对于外观的提取,我们可以使用getAppearanceMode()取得,以此类推,需要修改/得到相应的参数只需进行相应的set/get操作即可。
我们可以把Item和其他的高级UI部分结合起来,这样也是对我们学习的一种促进:
import javax.microedition.lcdui.*;
import javax.microedition.midlet.MIDlet;
public class StringItemDemo
extends MIDlet
implements CommandListener, ItemCommandListener {
private Display display;
private Form mainForm;
private final static Command CMD_GO = new Command("Go", Command.ITEM, 1);
private final static Command CMD_PRESS = new Command("Press", Command.ITEM, 1);
private final static Command CMD_EXIT = new Command("Exit", Command.EXIT, 1);
protected void startApp() {
display = Display.getDisplay(this);
mainForm = new Form("String Item Demo");
mainForm.append("This is a simple label");
StringItem item = new StringItem("This is a StringItem label: ",
"This is the StringItems text");
mainForm.append(item);
item = new StringItem("Short label: ", "text");
mainForm.append(item);
item = new StringItem("Hyper-Link ", "hyperlink", Item.HYPERLINK);
item.setDefaultCommand(CMD_GO);
item.setItemCommandListener(this);
mainForm.append(item);
item = new StringItem("Button ", "Button", Item.BUTTON);
item.setDefaultCommand(CMD_PRESS);
item.setItemCommandListener(this);
mainForm.append(item);
mainForm.addCommand(CMD_EXIT);
mainForm.setCommandListener(this);
display.setCurrent(mainForm);
}
public void commandAction(Command c, Item item) {
if (c == CMD_GO) {
String text = "Go to the URL...";
Alert a = new Alert("URL", text, null, AlertType.INFO);
display.setCurrent(a);
} else if (c == CMD_PRESS) {
String text = "Do an action...";
Alert a = new Alert("Action", text, null, AlertType.INFO);
display.setCurrent(a);
}
}
public void commandAction(Command c, Displayable d) {
destroyApp(false);
notifyDestroyed();
}
protected void destroyApp(boolean unconditional) {
}
protected void pauseApp() {
}
}
3.6.2 ImageItem下面我们来看ImageItem,ImageItem和StringItem其实区别仅仅在于一个是显示图像,一个是文字,它同样有两个构造函数,其中用到最多的是5个参数的构造函数,第一个是该Item的Label,第二个是图片,第三个是等效线,第四个是取代的文字(图片无法现实时),第五个是外观(和StringItem相同)。
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
public class ImageItemMIDlet extends MIDlet
implements ItemCommandListener
{
private Display display;
public ImageItemMIDlet()
{
display = Display.getDisplay(this);
}
public void startApp()
{
Image img = null ;
try
{
img = Image.createImage("/pic.png") ;
}catch(Exception e){}
Form f = new Form("ImageItem测试") ;
f.append(img) ;
ImageItem ii1 =
new ImageItem("图片1",img,
Item.LAYOUT_CENTER|Item.LAYOUT_NEWLINE_BEFORE,"图片1取代文字",Item.BUTTON);
f.append(ii1) ;
ImageItem ii2 =
new ImageItem("图片2",img,
Item.LAYOUT_RIGHT|Item.LAYOUT_NEWLINE_BEFORE,"图片2取代文字",Item.HYPERLINK);
f.append(ii2) ;
display.setCurrent(f);
}
public void commandAction(Command c,Item i)
{
System.out.println(c.getLabel());
System.out.println(i.getLabel());
}
public void pauseApp()
{
}
public void destroyApp(boolean unconditional)
{
}
}
3.7 CustomItemCustomItem是Item中一个比较重要的子类,它最大的优点是提高了Form中的可交互性。它和Canvas有很大的相似处。我们通过改写CustomItem可以实现完全控制在新的子类中条目区域的显示,它可以定义使用的颜色,字体和图形,包括特殊高亮的条目可能有的所有的焦点状态,只有条目的Label是有系统控制生成的,但是Label总是生成在CustomItem的内容区域外。
每个CustomItem都负责把它的行为与目标设备上可用的交互模式匹配。一个CustomItem调用方法来观察特定设备所支持的交互模式,这个方法会返回一个支持模式的位标记,支持的模式会的返回对应位会被设置,不支持的不被设置。
CustomItem一个比较重要的特性即是Form内部的遍历,即实现可能临时把遍历的责任委派给Item本身,这样可以实现特殊的高亮,动画等等效果。
由于customItem在Form类里扮演很重要的角色,其内容很庞杂,我们通过三个代码段来教读者如何使用CustomItem,希望大家通过对代码的深刻认识,提高自己对CustomItem的掌握程度。
import javax.microedition.lcdui.*;
import javax.microedition.midlet.MIDlet;
public class CustomItemDemo extends MIDlet implements CommandListener {
private final static Command CMD_EXIT = new Command("Exit", Command.EXIT, 1);
private Display display;
private boolean firstTime;
private Form mainForm;
public CustomItemDemo() {
firstTime = true;
mainForm = new Form("Custom Item");
}
protected void startApp() {
if(firstTime) {
display = Display.getDisplay(this);
mainForm.append(new TextField("Upper Item", null, 10, 0));
mainForm.append(new Table("Table", Display.getDisplay(this)));
mainForm.append(new TextField("Lower Item", null, 10, 0));
mainForm.addCommand(CMD_EXIT);
mainForm.setCommandListener(this);
firstTime = false;
}
display.setCurrent(mainForm);
}
public void commandAction(Command c, Displayable d) {
if (c == CMD_EXIT) {
destroyApp(false);
notifyDestroyed();
}
}
protected void destroyApp(boolean unconditional) {
}
protected void pauseApp() {
}
}
import javax.microedition.lcdui.*;
public class Table
extends CustomItem
implements ItemCommandListener {
private final static Command CMD_EDIT = new Command("Edit",
Command.ITEM, 1);
private Display display;
private int rows = 5;
private int cols = 3;
private int dx = 50;
private int dy = 20;
private final static int UPPER = 0;
private final static int IN = 1;
private final static int LOWER = 2;
private int location = UPPER;
private int currentX = 0;
private int currentY = 0;
private String[][] data = new String[rows][cols];
// Traversal stuff
//indicating support of horizontal traversal internal to the CustomItem
boolean horz;
//indicating support for vertical traversal internal to the CustomItem.
boolean vert;
public Table(String title, Display d) {
super(title);
display = d;
setDefaultCommand(CMD_EDIT);
setItemCommandListener(this);
int interactionMode = getInteractionModes();
horz = ((interactionMode & CustomItem.TRAVERSE_HORIZONTAL) != 0);
vert = ((interactionMode & CustomItem.TRAVERSE_VERTICAL) != 0);
}
protected int getMinContentHeight() {
return (rows * dy) + 1;
}
protected int getMinContentWidth() {
return (cols * dx) + 1;
}
protected int getPrefContentHeight(int width) {
return (rows * dy) + 1;
}
protected int getPrefContentWidth(int height) {
return (cols * dx) + 1;
}
protected void paint(Graphics g, int w, int h) {
for (int i = 0; i <= rows; i++) {
g.drawLine(0, i * dy, cols * dx, i * dy);
}
for (int i = 0; i <= cols; i++) {
g.drawLine(i * dx, 0, i * dx, rows * dy);
}
int oldColor = g.getColor();
g.setColor(0x00D0D0D0);
g.fillRect((currentX * dx) + 1, (currentY * dy) + 1, dx - 1, dy - 1);
g.setColor(oldColor);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (data[i][j] != null) {
// store clipping properties
int oldClipX = g.getClipX();
int oldClipY = g.getClipY();
int oldClipWidth = g.getClipWidth();
int oldClipHeight = g.getClipHeight();
g.setClip((j * dx) + 1, i * dy, dx - 1, dy - 1);
g.drawString(data[i][j], (j * dx) + 2, ((i + 1) * dy) - 2,
Graphics.BOTTOM | Graphics.LEFT);
// restore clipping properties
g.setClip(oldClipX, oldClipY, oldClipWidth, oldClipHeight);
}
}
}
}
protected boolean traverse(int dir, int viewportWidth, int viewportHeight,
int[] visRect_inout) {
if (horz && vert) {
switch (dir) {
case Canvas.DOWN:
if (location == UPPER) {
location = IN;
} else {
if (currentY < (rows - 1)) {
currentY++;
repaint(currentX * dx, (currentY - 1) * dy, dx, dy);
repaint(currentX * dx, currentY * dy, dx, dy);
} else {
location = LOWER;
return false;
}
}
break;
case Canvas.UP:
if (location == LOWER) {
location = IN;
} else {
if (currentY > 0) {
currentY--;
repaint(currentX * dx, (currentY + 1) * dy, dx, dy);
repaint(currentX * dx, currentY * dy, dx, dy);
} else {
location = UPPER;
return false;
}
}
break;
case Canvas.LEFT:
if (currentX > 0) {
currentX--;
repaint((currentX + 1) * dx, currentY * dy, dx, dy);
repaint(currentX * dx, currentY * dy, dx, dy);
}
break;
case Canvas.RIGHT:
if (currentX < (cols - 1)) {
currentX++;
repaint((currentX - 1) * dx, currentY * dy, dx, dy);
repaint(currentX * dx, currentY * dy, dx, dy);
}
}
} else if (horz || vert) {
switch (dir) {
case Canvas.UP:
case Canvas.LEFT:
if (location == LOWER) {
location = IN;
} else {
if (currentX > 0) {
currentX--;
repaint((currentX + 1) * dx, currentY * dy, dx, dy);
repaint(currentX * dx, currentY * dy, dx, dy);
} else if (currentY > 0) {
currentY--;
repaint(currentX * dx, (currentY + 1) * dy, dx, dy);
currentX = cols - 1;
repaint(currentX * dx, currentY * dy, dx, dy);
} else {
location = UPPER;
return false;
}
}
break;
case Canvas.DOWN:
case Canvas.RIGHT:
if (location == UPPER) {
location = IN;
} else {
if (currentX < (cols - 1)) {
currentX++;
repaint((currentX - 1) * dx, currentY * dy, dx, dy);
repaint(currentX * dx, currentY * dy, dx, dy);
} else if (currentY < (rows - 1)) {
currentY++;
repaint(currentX * dx, (currentY - 1) * dy, dx, dy);
currentX = 0;
repaint(currentX * dx, currentY * dy, dx, dy);
} else {
location = LOWER;
return false;
}
}
}
} else {
//In case of no Traversal at all: (horz|vert) == 0
}
visRect_inout[0] = currentX;
visRect_inout[1] = currentY;
visRect_inout[2] = dx;
visRect_inout[3] = dy;
return true;
}
public void setText(String text) {
data[currentY][currentX] = text;
repaint(currentY * dx, currentX * dy, dx, dy);
}
public void commandAction(Command c, Item i) {
if (c == CMD_EDIT) {
TextInput textInput = new TextInput(data[currentY][currentX], this,
display);
display.setCurrent(textInput);
}
}
}
import javax.microedition.lcdui.*;
import javax.microedition.midlet.MIDlet;
public class TextInput extends TextBox implements CommandListener {
private final static Command CMD_OK = new Command("OK", Command.OK,
1);
private final static Command CMD_CANCEL = new Command("Cancel", Command.CANCEL,
1);
private Table parent;
private Display display;
public TextInput(String text, Table parent, Display display) {
super("Enter Text", text, 50, TextField.ANY);
this.parent = parent;
this.display = display;
addCommand(CMD_OK);
addCommand(CMD_CANCEL);
setCommandListener(this);
}
public void commandAction(Command c, Displayable d) {
if (c == CMD_OK) {
// update the table's cell and return
parent.setText(getString());
display.setCurrentItem(parent);
} else if (c == CMD_CANCEL) {
// return without updating the table's cell
display.setCurrentItem(parent);
}
}
}
3.8 TextField和DateFieldTextField和我们前面讲的TextBox大同小异,只是它是作为Form的一个子类存在,而TextBox则是和Form平起平坐,因此我们直接给出代码方便大家的学习。
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
public class TextFieldWithItemStateListenerMIDlet extends MIDlet
implements ItemStateListener
{
private Display display;
public TextFieldWithItemStateListenerMIDlet()
{
display = Display.getDisplay(this);
}
TextField name ;
TextField tel ;
TextField summary ;
public void startApp()
{
Form f = new Form("TextField测试") ;
name = new TextField("姓名","",8,TextField.ANY) ;
tel = new TextField("电话","",14,TextField.PHONENUMBER) ;
summary = new TextField("总结","",30,TextField.UNEDITABLE) ;
f.append(name) ;
f.append(tel) ;
f.append(summary) ;
f.setItemStateListener(this);
display.setCurrent(f);
}
public void itemStateChanged(Item item)
{
if(item==name)
{
summary.setString("输入的姓名为:"+name.getString());
}else if(item==tel)
{
summary.setString("输入的电话为:"+tel.getString());
}else
{
summary.setString("");
}
}
public void pauseApp()
{
}
public void destroyApp(boolean unconditional)
{
}
}
DateField的目的是方便用户输入时间,它的构造函数共有三个参数,一个是Label,一个是输入模式,一个是java.util.TimeZone对象,也可以省去第三个参数,只使用前两个。
3.9 Gauge和Spacer,ChoiceGroup3.9.1 GaugeAlert有一套方法可以显示进度,利用setIndicator()/getIndicator()这组函数,可以显示进度的画面。Gauge的最大用处就是拿来当进度显示使用。
拿来当进度显示用的Gauge对象必须满足如下要求:
l 控制与用户交互的构造函数的第二个参数必须为flase
l 不能被其他的Form或者Alert使用
l 不能加入Command
l 不能有Label
l 不能自己设定等效线的位置。
l 不能自己设定组件的大小。
大家可以参考如下的代码:
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
public class AlertWithIndicatorMIDlet extends MIDlet
implements CommandListener
{
private Display display;
public AlertWithIndicatorMIDlet()
{
display = Display.getDisplay(this);
}
Gauge g ;
public void startApp()
{
Alert al = new Alert("处理中");
al.setType(AlertType.INFO);
al.setTimeout(Alert.FOREVER);
al.setString("系统正在处理中");
g = new Gauge(null,false,10,0) ;
al.setIndicator(g);
Command start = new Command("开始",Command.OK,1) ;
Command stop = new Command("停止",Command.STOP,1) ;
al.addCommand(start);
al.addCommand(stop);
al.setCommandListener(this);
display.setCurrent(al);
}
public void commandAction(Command c,Displayable s)
{
String cmd = c.getLabel() ;
if(cmd.equals("开始"))
{
for(int i=0 ; i <11 ; i++)
{
g.setValue(i);
try
{
Thread.sleep(500);
}catch(Exception e){}
}
}else if(cmd.equals("停止"))
{
notifyDestroyed() ;
}
}
public void pauseApp()
{
}
public void destroyApp(boolean unconditional)
{
}
}
3.9.2 SpacerSpacer的用处很简单,就是加一处空白,大家可以参考API文档进行实际开发。
3.9.3 ChoiceGroupChoiceGroup和List大同小异,因为二者都实现了Choice接口,所以在很多地方是一样的,但是请注意一点,在这里我们不能使用Choice.IMPLICIT类型,只能用Choice.EXCLUSIVE,Choice.MUTIPLE,Choice.POPUP,三种类型,与List的区别即第三种弹出式菜单。