第一章 J2ME开发简介
目标:
掌握J2ME的基本概念
了解CLDC特性
对手机开发有整体上的认识
1.1 J2ME基本概念
手机上的应用软件经历了3个阶段:
z 仅提供基本通话功能
z 简单的应用程序(电话簿)
z 网络功能(WAP)
使用人群的增长,手机厂商依然闭门造车,导致2个问题的出现:
z 没有开放的平台,各自的软件不能相互使用。
z 访问Internet只能通过WAP方式。
J2ME的出现解决了遇到的问题:
z J2ME可以跨平台,可以开发可移植性好的无线应用程序。为目前发展迅速的手机游戏市场提供了契机。
z J2ME提供了HTTP、TCP等高级的网络协议访问网络的方式。
发展趋势:
无线移动和嵌入式市场将是互联网未来竞争的焦点。目前,Java技术在网络和嵌入式的应用非常广泛。
有关数据统计,Java技术已经应用于2.5亿部移动设备上。预计到2007年,100%的手机将采用Java技术;
%63的开发商认为,对于嵌入式和小型器件,Java技术是第一通用平台,而J2ME就是这一通用平台的重要开发技术平台。
在大家正式认识Java 2 Micro Edition之前,我们先为各位介绍目前所有支持Java 2 Micro Edition的行动电话,我们可以发现不管是欧、美、日大厂的手机都开始大力支持Java 2 MicroEdition , 藉此介绍可以让大家体认, 为何了解Java 2 Micro Edition,以及学习如何用Java撰写手机上应用程序肯定是一个无可避免的趋势。
Sony Ericsson
Nokia
Motorola
Siemens
应用领域:
适合各类小型嵌入式设备以及消费电子类产品。
应用前景:
J2ME凭借广阔的运行平台、良好的可移植性以及较短的开发周期成为软件市场上较为热门的技术,并备受服务提供商和电信运营商的青睐。
手机硬件水平的不断提高以及娱乐功能的增强,势必突显J2ME在手机应用以及游戏软件方面的技术特长。
1.1.1 J2ME简介
Java是1995年6月由Sun公司推出的,具有易用性、平台无关性、易移植性等诸多特征。Java语言的特点使其得到了广泛应用。Java语言程序开发费用少,工作效率高,拥有良好的用户界面和强大的开发工具。
Java语言的前身是Oak项目,原本就是为嵌入式领域设计的,但却没有顺利进入嵌入式领域,而是随着Internet的
发展占领了PC端以及Server端后,又回到了嵌入式领域。
用于嵌入式的Java称为Java 2 Micro Edition,简称J2ME,即Java 2微型版。
使用J2ME进行无线应用程序开发还具有许多Java本身的优点:
1. 平台无关性
引进虚拟机原理,并运行于虚拟机。Java的数据类型与机器无关,JVM是建立在硬件和操作系统之上,实现二进制代码的解释执行功能。
2. 安全性
舍弃了C++的指针,避免了对存储器地址的直接访问。
对程序提供了安全管理器,防止程序的非法访问。
字节码验证过程保证了程序不能访问内存空间或使用其域外的资源,字节码验证程序还防止应用程序重载Java语言核心库,这是一种可以用来绕过其他应用程序级安全性措施的方法。
J2ME使用网络安全SSL协议,可以确保无线传输中的信息安全
3. 面向对象
使得程序员可以把主要精力用在类和接口的设计和应用上。
4. 分布式
J2ME建立在扩展无线网络平台上。库函数提供了无线连接协议传送和接收信息的方法,
使得程序员使用网络上的文件和使用本机文件一样容易。
5. 图形界面和多媒体功能
J2ME提供了丰富的用户界面和事件处理功能,同时提供了游戏、视频以及音效的开发功能。
1.1.2 JVM、CVM和KVM的介绍
J2ME广阔的运行平台,良好的可移植性是通过将字节码文件运行于Sun的K虚拟机(KVM)上实现的。
JVM:Java 2标准版Java虚拟机,主要使用在J2EE和J2SE中,应用在系统资源相当丰富的设备上。32M
KVM:是一个专门为小型或者资源受限设备所设计的紧凑的、便携的虚拟机。通常只有128K或者更小。
CVM:是为用户和嵌入式设备设计的完全功能性VM,功能比KVM更加强大。
CVM和KVM是由JVM演变过来的。
MIDlet(移动信息设备小程序)主要使用KVM。
J2ME应用程序并不一定只能使用KVM,J2ME技术可以使用任何虚拟机,不过至少应当具有KVM所实现的功能。
1.1.3配置——Configuration
移动设备厂商众多,而且功能和具体接口都不相同,J2ME在开发的通用性方面是通过一个特殊的配置文件来实现的。J2ME将不同的设备进行分类,形成了一定的规范。不同设备的开发需要遵循这些规范。每个配置为一组通用设备提供最小的Java平台,这些配置文件就是对具体的硬件类型进行配置,方便J2ME辨认不同类型设备。
J2ME大致将嵌入式设备区分为两种:
z 资源有限,包括运算功能有限、电力供应有限、联机速度有限或是屏幕显示大小与色彩有限的嵌入式设备。
z 资源相对无限设备,通常运算能力较好,并且在电力供应、联机速度上相对较充足的嵌入式设备。例如:机顶盒、微波炉等。
思考:手机属于哪一类呢?
可以把Configuration当作是J2ME规定嵌入式设备能够执行Java程序的规范(或者说是对在目标设备上运行Java程序的最低要求)。定义了这些设备至少要符合的运算能力、供电能力、内存大小等。同时也定义了一组在这些设备上执行Java程序所能使用的类库。
(1) Connected limited device configuration(有限连接设备配置,简称CLDC):
Connected limited device指那些运算能力有限、电力供应有限或者联机速度有限的设备。
Connected limited device configuration规定了上述设备若执行Java程序,则必须满足某些特定条件(CPU、内存、联机速度等),这些条件就定义在CLDC中。
(2) Connected device configuration(连接设备配置,简称CDC):
可连接、资源相对无线设备,这类设备运行Java程序的要求定义在CDC中。
CLDC与CDC不同在于他们面向的具体设备配置不同,与个人电脑相比,CLDC针对的设备的处理器能力有限,并且存储器大小往往只有128KB到512KB之间。CDC针对较好的设备,可能有32位或者64位处理器,至少拥有512K存储空间。
不同类型和配置的硬件会使用不同的虚拟机。基于CDC的系统会使用一个功能强劲的虚拟机CVM,而基于CLDC的系统使用KVM虚拟机。
CDC和CLDC开发类都使用了一部分J2SE的开发类,并把这些类进行了优化。同时还添加了一部分针对移动设备而开发的类。而CLDC则完全是在CDC开发类的基础上进行优化的,没有添加自身需要的类。
CLDC与CDC的范围
配置里面所定义的类库除了Java标准核心类库的子集合之外,也加入了与该类型设备特性相符的扩充类库。就CLDC来说,核心类库为java.lang.*、java.io.*、java.util.*,而支持的扩展类库为javax.microedtion.io.*;
CDC 1.0和CDC 1.1在核心类库上和J2SE相差无几。但CLDC1.0不支持浮点数(float、double)。CLDC1.1加强了CLDC1.0的功能,提供了对浮点运算的支持(对float、double类的支持,增加了java.lang.Float、java.lang.Double两种工具类)等。
增加了功能,使得运行时的最小内存从CLDC1.0的160KB提升到了192KB。
1.1.4 J2ME简表——Profile
是架构在Configuration之上的规范。每种Profile都会声明它是基于那种Configuration的。
Profile的出现,是为了要更明确的区分出各种嵌入式设备之上的Java程序该如何开发,以及它们应该具有哪些功能。换句话说,Profile就是为了开发人员能够根据具体设备而选择不同的开发包,开发出和硬件结合更好的软件。
通常Java程序在各种嵌入式设备上的用户界面该如何呈现就是定义在Profile里(例如,
MIDP之中的LCDUI,Limited Configuration Device User Interface)。Profile中也定义了程序该如何和用户或设备产生互动以及特定种类嵌入式设备的最小设备需求。
既然Profile建立于Configuration之上,其意义就是说Profile之中所规范的配置需求不可能比Configuration低。
简表为生产相同电子设备的不同生产商提供了标准化的Java类库。MIDP(Mobile Information Device Pofile)是CLDC实现的第一个简表。有1.0和2.0两个版本。针对移动设备(如寻呼机、手机等)所定义。
J2ME的体系结构
简表、配置文件和虚拟机基本是三层关系,上一层总是运行在下一层的基础之上,而每个配置则分别拥有3种类型的简表文件。
1.1.5 J2ME的Java企业系统体系结构
Java虚拟机是J2ME技术的核心,配置和简表主要是为具体的移动设备或其它配置的硬件提供开发所用的类或接口。配置主要针对一个大类型的设备,而简表则为那些更为具体的设备提供说明。
写手机应用程序,需要两个前提:CLDC配置和MIDP简表。一般是CLDC配置捆绑了虚拟机和一套针对开发平台所能够使用的Java类库。一个MIDP简表来为开发平台提出使用了哪些附加的Java类,如指出使用了哪些用户界面、输入或数据库类等。
第二章 J2ME开发环境配置
目标:
z MIDP开发包的安装和配置
z 掌握MIDP开发工具的安装
z 了解MIDP开发工具的选择
z 掌握使用开发工具编写一个简单程序
2.1一般的J2ME开发步骤如下:
z 按需求设计移动应用程序
z 使用IDE和设备模拟程序对应用程序进行编码
z 在真实的设备上测试和部署应用程序
2.2 MIDP开发所需要的资源和工具
JDK
WTK2.2
netbeans-4.1
NetBeans Mobility Pack
或者
JDK
WTK2.2
Eclipse
EclipseMe
针对支持J2ME CLDC/MIDP的移动设备卡法工具叫J2ME Wireless Toolkit,目前最新版本为2.2。该工具包提供了开发手机程序的一个模拟环境(运行所需的库及模拟器等)。
补充:
linux下netbeans中文乱码问题的解决办法
进入javahome的目录 例如:
cd /usr/local/javahome/jre/lib/fonts/
mkdir fallback
然后从windows的目录目录Fonts下拷贝 simsum.ttc 到
/usr/local/javahome/jre/lib/fonts/fallback/
并改名为simsum.ttf
2.2.1 安装和配置J2SE SDK开发包
设置两个环境变量:
z JAVA_HOME
z PATH
2.2.2 安装和配置J2ME Wireless Toolkit开发包
安装WTK之前必须已经安装了J2SE SDK。
2.3开发工具的安装与配置
第三章 开发MIDP应用程序
常用名词:
z MIDlet:一个可以执行的应用程序的基本单位。除了继承自javax.microedition.midlet.MIDlet之外,还包括让此类可以顺利执行的所有其它类和资源文件(只要是非class文件都称作资源文件)所构成的集合。
z MIDlet Suite: MIDlet所构成的集合,叫做MIDlet应用程序套件。一个MIDlet Suite中可以存在多个MIDlet。一般来说,MIDlet Suite之中常常只有一个MIDlet。应用程序部署时,都以MIDlet Suite为单位进行。
? Jar文件(.jar文件)
包裹住MIDlet Suite的文件,属于ZIP压缩格式。
? 描述文件(.jad文件)
用来描述MIDlet Suite基本信息的文本文件。如,类名称、程序名、文件大小等。
是一个外部文件(不存在JAR文件内部,独立存在的文件)。
? 应用程序管理器(Java Application Manager, JAM)
负责将MIDlet Suite安装到机器上执行以及管理MIDlet生命周期的机制或软件。
应用程序管理器负责管理在设备上的所有的J2ME应用程序。
3.1 MIDlet基本程序框架
MIDlet必须继承自javax.microedition.midlet.MIDlet类。
javax.microedition.midlet.MIDlet类中定义了三个抽象方法,分别是:
z startApp() 转至运行状态
z pauseApp()转至暂停状态
z destroyApp()转至销毁状态
JAM通过这三个抽象方法来控制MIDlet的生命周期。
一个基本的MIDlet:
import javax.microedition.midlet.*;
public class FirstMIDlet extends MIDlet
{
public FirstMIDlet()
{
}
public void startApp()
{
}
public void pauseApp()
{
}
public void destroyApp()
{
}
}
一旦MIDlet被JAM加载,首先会调用MIDlet中没有参数的构造函数以进行初始化。若有必要编写有参数的构造函数时,应另外别写一个无参数的构造函数,否则MIDlet将无法正确地初始化,也就无法顺利地启动。
MIDlet的起始行为
使用Display.getDisplay(this)取得代表该设备显示画面的Display对象。MIDlet的生命周期中不管何时调用Display.getDisplay(this)取得的都是同一份Display对象的引用。
每个MIDlet都有一份逻辑上只属于自己的Display(这就是为什么要传入MIDlet引用作为getDisplay()参数的原因),就算是同一个MIDlet suite中的MIDlet,或是同一时间在同一个VM运作的MIDlet,所取得的也会是不同的Display对象。
设定显示在屏幕上的画面:
display.setCurrent(Displayable 类的子类实体)
练习:Form就是一个Displayable类的子类,将Form显示与屏幕。
提示:1、import javax.microedition.lcdui.* ;
2、Form f = new Form(“MyForm”);
注意:MIDlet只能由应用程序管理器产生。不能自己在程序里new出其它的MIDlet。
3.2 MIDlet 的生命周期
当一个MIDlet 成功地初始化之后,就开始展开了它的生命周期。底下这张图,描述一个MIDlet 的生命周期,MIDlet 的生命周期完全由Java Application Manager 控制,也就是说,当MIDlet 要从一个状态变成另外一个状态时,Java Application Manager 会呼叫对应的函式,如果状态转换时发生错误,那么Java Application Manager会丢出MIDletStateChangeException 例外。
Midlet的生命周期
看过上面这张图,我们先给大家一个概念,那就是:只有当Java Application Manager 认为MIDlet 的状态必须改变时,才会呼叫图中的相关函式, 这些函式呼叫成功之后, Java Application Manager 才会改变MIDlet 的状态。因此,如果MIDlet 自己叫用这些函式,并不会发生错误,但是也不会造成状态的转换,只是一个单纯的函式呼叫而已。
从图中我们可看出startApp()很可能不光只有被呼叫一次而已,而是每次从停止状态重新回到运作状态的时候都会被Java Application Manager 呼叫。所以只需要被初始化一次的变量就不适合在startApp()之中做初始化,请改用建构式做初始化动作。
其实MIDlet 自己本身也可控制自己的状态,但是不是自己改变自己的状态,而是先自己呼叫上述相对应的状态改变函式,然后发出讯息通知Java Application Manager , 请它来帮我们改变MIDlet 的状态,方法如下图所示:
图3.4.3 Midlet的状态变化
对照以上两张图,举个例子大家可能会比较清楚:假设今天如果是MIDlet 主动要将MIDlet 的状态由运作状态变成停止状态,那么我们光是直接呼叫pauseApp()函式,只会执行pauseApp()之中的程序代码,却不会改变MIDlet 的状态,因此在呼叫pauseApp()之后,MIDlet 要另外呼叫notifyPaused()以通知Java Application Manager,Java Application Manager 收到通知之后, 才会让MIDlet 进入停止状态。
从这里我们可看出startApp()、pauseApp()以及destroyApp()并非控制MIDlet 生命周期的函式,他们只是个提供我们初始化资源、释放资源的地方而已。
根据MIDP 1.0 规格书中所说,即使MIDlet 处于停止状态,但是它仍然可以处理异步事件( Asynchronous Event ), 比方说Timer 的事件或其它回呼函式(callback)。
从以上几张图中可看出,只要MIDlet 进入了毁灭状态,就无法再回头。但是根据规格, 我们无法在MIDlet 之中直接呼叫System.exit()或Runtime.exit()来结束程序的执行。
如果您这样做的话会引发java.lang.SecurityException 异常。MIDlet 一定要自己先呼叫destroyApp(),然后再呼叫notifyDestroyed(),请Java Application Manager 帮我们将MIDlet 转换到毁灭状态,然后结束MIDlet 的运作。单单MIDlet 自己呼叫destroyApp()根本无济于事,作用就如同单纯的函式呼叫一般,完全不会改变MIDlet 的状态,这点请大家务必注意。
3.3 MIDP Profile的介绍
虽然每一个MIDlet程序都可以单独运行,但是要成功的在手机上运行,需要把许多相关的MIDlet程序封装在一个单独的jar文件中,就是说把一个MIDlet Suite封装成为了一个Jar文件。由于多个MIDlet程序被封装在一起,为了能够让手机的操作系统辨别具体的每个程序,就必须有特殊的配置文件,在配置文件中说明运行这些程序所需要的硬件和软件平台。
一般手机程序开发,配置文件有2个:MANIFEST文件和JAD文件。其中jar文件中的MANIFEST文件用来描述MIDlet Suite的配置,例如:MIDlet的名称、版本、开发商等。
而JAD文件则说明了具体的运行配置,以及Jar文件所在的位置等等信息。
MANIFEST.MF文件:
表1
属性
用途
MIDlet-Name
MIDlet程序包的名称。例如“Game Pack”
MIDlet-Version
MIDlet的版本号
MIDlet-Vendor
MIDlet的创建者或提供商
MIDlet-Icon
应用程序管理器把这个图标与MIDlet-Name相关联,这是一个图形文件,以PNG图象格式储存。
MIDlet-Description
描述MIDlet的文本
MIDlet-Info-URL
可能提供更多MIDlet和/或供应商信息的URL
MIDlet-??
这个属性包括三段信息: ??MIDlet名称 ??用于这个MIDlet的图标(可选) ??应用程序管理器将调用来加载这个MIDlet的类名 在我们的“ Game Pack”例子中,有两个条目: MIDlet-1: KOF, /images/kof.png, kof.kofMIDlet MIDlet-2: Golf, /images/golf.png, golfMIDlet
MIDlet-Jar-URL
JAR文件的URL
MIDlet-Jar-Size
JAR文件的大小
MIDlet-Data-Size
持久数据存储必需的最小字节数
MicroEdition-Profile
MIDlet需要哪一种J2ME简表
MicroEdition-Configuration
MIDlet需要哪一种J2ME配置
清单文件及描述文件的必须属性值
清单文件中应该包含的项目
z MIDlet-Name
z MIDlet-Version
z MIDlet-Vendor
z MIDlet-
z MicroEdition-Profile
z MicroEdition-Configuration
除了Jar内部有清单文件外,Jar文件外部有时会有个描述文件(.jad)。
描述文件的作用只是单纯地让应用程序管理能够在下载Jar或者安装Jar之前,先做些检查,确定机器上的环境是否符合Jar文件所需,是否符合用户的需求。
(Jar文件的mime类型是application/java-archive)
描述文件必须属性
z MIDlet-Name
z MIDlet-Version
z MIDlet-Vendor
z MIDlet-Jar-URL
z MIDlet-Jar-Size
通过比较,会发现清单文件或描述文件一定要制定三个相同的参数:
z MIDlet-Name
z MIDlet-Version
z MIDlet-Vendor
若清单文件中这三个参数有任一个和描述文件的这三个参数不同,JAM将不会下载并安装该Jar文件。JAM靠这三个属性辨别机器中所有的MIDlet suite。
MIDP程序设计进阶
系统参数的提取
MIDP在运行时,可以通过java.lang.System.getProperty()函数来取得系统属性。这些属性可以让MIDlet了解它们所处环境的相关信息。
1. microedition.profile:取得系统所支持的所有Profile信息。
2. microedition.configuration:取得系统所支持的Configuration信息。
3. microedition.locale:取得系统目前所使用的地区信息。
4. microedition.platform:MIDlet所在平台(机器)的名称或型号。
5. microedition.encoding:取得系统缺省使用的语言编码信息。
练习:写个系统探针的程序。
字符串与基本类型的转换
MIDP运行时,可以通过midlet.getAppProperty()取得清单文件或者描述文件之中的属性值。字符串到基本类型的转换,需要依靠java.lang中定义的Byte、Short、Integer、Long类中的parseXXX()方法。注意,这四个类都是处理整数类型,只有范围上的不同,若字符串转换成数值后超过相应类型的取值范围,会产生异常。
CLDC1.0不支持浮点数,没有对应浮点数的类,只有整数类型的类。
CLDC1.1之后开始支持浮点数,新增了Float、Double两个类。
练习:为MIDlet添加属性并提取MIDlet-Name、Vendor、Version等信息。
随机数
API:java.util.Random(只能是int或long类型的随机数)。
构造:Random() //会在构造方法内部调用this(System.currentTimeMillis())作为种子
Random(seedvalue) seedvalue是随机数种子。
方法:
setSeed() 设置随机数种子
nextInt() 产生int型随机数
netLong() 产生long型随机数
nextInt()产生的数值范围(-231~231-1)。
nextLong()产生的数值范围(-263~263-1)。
这两个方法在内部均是调用其next()方法,此方法需要一个参数,如果给定11,就会产生一个11个bit的随机数。因此,nextInt()的实现为:
public int nextInt()
{
return next(32);
}
nextLong的实现为:
public long nextLong()
{
return ((long)next(32) <<32) + next(32));
}
自定义随机数产生范围:
例:
产生-100~100之间的数值
int ret = random.nextInt()%100;
产生0~100之间的数值(int本身是有符号数,32bit,必须把第一bit设定为0作为正数。
利用无号移位运算符
int ret = (random.nextInt()>>>1)%100;
或
int ret = (random.nextInt() & 0x7FFFFFFF)%100;
产生-100~0之间的随机数,则把第一bit设置为1。
可以利用在产生正数的方法前加负号的方法:
int ret = -(random.nextInt()>>>1)%100;
或
int ret = (random.nextInt() | 0x80000000)%100;
练习:产生随机数
执行时间的测量
可以利用System.currentTimeMillis()取得当前时间与1970年1月1日零时UTC时间(协调世界时)的差距,返回long型值,表示毫秒(千分之一秒)。
程序任意两个位置调用System.currentTimeMillis(),所得的值相减,即经过的毫秒数。
日期处理
API: java.util.Calendar、java.util.Date、 TimeZone是三个用来处理与日期相关功能的类。
z TimeZone代表时区
全球统一时间UTC(协调世界时),在世界任意地方时间都一样。
根据每个地区所在位置不同,又定制了时区的概念。
我们所在的时区是CCT,与UTC的偏移量为+8 :00。
UTC与GMT(格林尼治)时间没有时间差,是相同的。其它时区都会与UTC或GMT有个时间差。
取得TimeZone,不能通过new。可以通过:
1. TimeZone.getDefault() 取得系统默认使用的时区。
2. TimeZone.getTimeZone(id)指定要使用的时区。
TimeZone方法:
1. useDaylightTime() 是否使用日光节约时间。
2. getRawOffset() 取得该TimeZone使用的时区和GMT相差多少。
3. static String[] getAvailableIDs() 取得系统所支持的时区。
取得的时区的结果会因不同设备的设定而有所不同。除非特殊目的,一般使用TimeZone.getDefault()取得系统缺省使用的时区。
z Date
Date类负责记录与1970年1月1日零时UTC时间(协调世界时)的差距。
构造:
Date date = new Date(time); time就是与1970年1月1日零时UTC时间的差距。
或
Date date = new Date(); 相当于调用了
Date date = new Date(System.currentTimeMillis());
功能:
Date类本身除了能够存放与1970年1月1日零时UTC时间的差距外,本身无其它功能。要从Date类解析出正确的年、月、日、星期、时、分、秒等信息必须依靠Calendar类才可以。
z Calendar
取得Calendar,不能通过new。
1. Calendar.getInstance() 取得系统默认时区所建立的Calendar。
2. Calendar.getInstance(TimeZone tz) 取得指定时区所使用的Calendar。
调用Calendar.getInstance()等同于调用Calendar.getInstance(TimeZone.getDefault())。
Calendar的方法:
1. setTime(date) 使用指定Date设定Calendar。
2. setTimeMillis(long) 使用long型值设定Calendar。
一般,在中国使用的设备在使用Calendar.getInstance()默认使用的时区都是CCT。若要强制使用CCT, 可以:
TimeZone tz = TimeZone.getTimeZone(“CCT”);
Calendar cctcal = Calendar.getInstance(tz);
cctcal.setTime(new Date());
System.out.println(cctcal.getTime());
前提是CCT是设备支持的时区才行(TimeZone.getAvailableIDs())。
提示:可以直接在产生Date类时直接加上8(hr)x60(min)x60(sec)x1000(milsec)
注意:Calendar、Date与TimeZone将来都可以和LCDUI程序库中的DateField配合使用。
可以使用Calendar的方法get与set方法设定或取得部分信息。
get()方法:
1. int get(Calendar.AM_PM)
2. int get(Calendar.DAY_OF_MONTH)
3. int get(Calendar.DATE) //意义与DAY_OF_MONTH相同
4. int get(Calendar.DAY_OF_WEEK)
5. int get(Calendar.Hour_OF_DAY) //24时制
6. int get(Calendar.HOUR) //12时制
7. int get(Calendar.MINUTE)
8. int get(Calendar.SECOND)
9. int get(Calendar.MILLISECOND)
10. int get(Calendar.YEAR)
11. int get(Calendar.MONTH)
其他Calendar的静态变量:
week相关:
Calendar.SUNDAY
Calendar.MONDAY
Calendar.TUESDAY
Calendar.WEDNESDAY
Calendar.THURSDAY
Calendar.FRIDAY
Calendar.SATUDAY
month相关:
Calendar.JANUARY
FEBRUARY
MARCH
APRIL
MAY
JUNE
JULY
AUGUST
SEPTEMBER
OCTOMBER
NOVEMBER
DECEMBER
set()方法:
1. cal.set(Calendar.YEAR, 2008);
Thread的使用
z Thread有两种建立的方法:
1、 继承Thread,重写run()方法;
2、 实现Runnable接口,实现run()方法。
z 启动Thread,使用其start()方法。
z 结束Thread,利用自定义的flag。
注意:CLDC里的Thread的功能没有J2SE中的多。
OTA发布程序
MIDlet程序的安装有两种方式:
z 通过数据线把程序安装到手机中
z OTA(Over the air)的传输方式。OTA表示MIDlet程序可以通过网站进行发布,用户可以通过输入网络地址,把MIDlet程序通过无线网络下载到本机上。智能手机直接通过无线电连接到服务器——空中下载。
一、属性设置
如果希望通过OTA发布,需要对JAD文件进行配置。具体步骤:
运行ktoolbar?打开项目?点击工具条上Setting按钮?在弹出对话框标签中选择Required标签?设置MIDlet-Jar-URL?输入网络地址,如http://127.0.0.1:8080/OTATest.jar --> 菜单Project>Package>CreatePackage菜单命令-->OTATest程序打包为jar文件。
二、服务器安装 Tomcat
1. 设置环境变量:
CLASSPATH 类路径,供JAVA编译器寻找类的位置
.;e:/jdk1.5/lib;e:/tomcat/lib
JAVA_HOME 由Tomcat判断java的安装路径
E:/jdk1.5
TOMCAT_HOME Tomcat本身的安装路径
E:/tomcat
PATH 让操作系统来寻找可执行程序的位置
把e:/tomcat/bin;e:/jdk1.5/bin;放入PATH
(启动tomcat startup.bat
停止tomcat shutdown.bat)
b) 建立工作目录
注意:一定要建立一个“/web-inf”子目录
应该有index文件(index.htm、index.jsp等等)
例:在tomcat目录中寻找到webapps目录,在该目录中添加一个新的自己的目录比如MyTest,该目录下需要有一个web-inf子文件夹,可将ROOT中的web-inf子文件夹直接拷贝过来,至此我们已经建立了一个空的应用程序目录。
三、在服务器上发布程序
要使手机用户可以通过OTA方式下载程序,需要在服务器上放置刚才打包的程序。
步骤:
z 将jar文件和jad文件拷贝到服务器相应路径。
z 新建或者修改html文件(假设为test.html)并在其中加入
OTATest
z 在浏览器中查看网页是否正常
四、通过OTA安装MIDlet程序
1. Project>Run via OTA
2. 按下右下角软键触发”Apps”按钮
3. 按下手机屏幕上的”Menu”按钮?Launch?按下Select键
4. 在屏幕上输入正确的下载地址http://127.0.0.1:8080/test.html
5. 选择Menu>Go
6. 按下Install
7. 按下Install
集成开发环境Eclipse的安装配置
J2ME简明教程( 第四章)
LCDUI
LCDUI——Limited Configuration Device UI。提供了用户接口。
1. LCDUI包的设计:
两种API:
z 高级API(Screen):画面具有可移植性,但无法决定组件的颜色、字体和外观。
z 低级API(Canvas):编写相对复杂,但对画面的控制具有完全的控制权。由于在不同的机器上有不同的特性(控制方式、屏幕颜色等),不能保证不同种类机器上可以得到相同的执行结果。
由于手机一次只能显示出一个画面,因此每个画面只能在高级API或者API之间择一使用。
高级API和低级API可以在同一个MIDlet中混合使用,组成应用程序中的每个画面。但是同一个画面中,就只能使用高级或者低级API中的一个。
2. LCDUI的包体系结构
能够显示在屏幕上的组件都是继承自抽象类Displayable。
Displayable
|
-------------Screen(高级API——高级类库)
|
-------------Canvas(低级API——低级类库)
LCDUI包全部定义在java.microedition.lcdui中。
3. Displayable类
是一个抽象类。其子类都具有显示于屏幕上的能力。
提供的方法:
getTicker()/setTicker()设定/获取跑马灯
setTitle()/getTitle()设定/获取画面标题
注意:上述方法在MIDP1.0中用于Screen,而在MIDP2.0中可用于Displayable中。
getHeight()取得屏幕上应用程序区的高度
getWidth()取得屏幕上应用程序区的宽度
上述2个方法,对低级API来说非常有用。因为每款手机的屏幕大小不同,而跑马灯与标题区存在与否,会影响这两个方法的返回值。
应用程序区:除去标题、跑马灯区域以及Command显示区以外,屏幕上剩下的那部分区域。
Displayable的子类可以通过isShow()判断目前这个画面是否正被Display显示在屏幕上。
Displayable类中定义了一个名为sizeChanged()的方法。可以被Canvas或Form的子类所重写(Override)。例如,Canvas的setFullScreenMode()方法就会导致sizeChanged()被调用。
第四章 高级界面开发
目标:
z 了解Screen的基本概念
z 了解基本屏幕类的开发
MIDP用户界面设计开发不同于一般的桌面程序,受限于屏幕大小、颜色、内存和处理能力。好的界面应该根据设备具体情况有针对性地设计。
4.1 MIDP高级界面开发
高级API提供了常用的控件,强调可移植性,只能有限控制控件外观,事件的使用也很有限。
4.1.1 Display类的开发
MIDP的程序界面是通过屏幕显示出来的。在屏幕上显示一幅画面就是Display对象要实现的功能。
每个MIDP程序都是由Displayable对象来具体处理在屏幕上显示的内容。MIDP程序可以根据手机使用者与程序的交互情况,把每次操作后的画面通过Displayable对象显示在当前屏幕上。
Displayable是Display的一个继承类,主要包括两个具体实现类:
z 高级界面开发类Screen
z 低级界面开发类Canvas
MIDP1.0中Displayable定义的方法有:
boolean isShown()
void addCommand(Command cmd)
void removeCommand(Command cmd)
void setCommandListener(CommandListener l)
MIDP2.0中Displayable新添加的方法有:
String getTitle()
public void setTitle(String s)
Ticker getTicker()
public void setTicker(Ticker ticker)
public int getWidth()
public int getHeight()
protected void sizeChanged(int w, int h)
上面的方法均可在Screen和Canvas中使用。但要注意在MIDP1.0下开发,MIDP2.0的方法不能够使用。
类Screen共有四个继承它的屏幕类,而Form屏幕类可以添加八个继承自Item类的控件类。
Screen
List
TextBox
Form
DateField
TextField
ImageItem
Gauge
ChoiceGroup
StringItem
CustomItem(MIDP2.0)
Spacer(MIDP2.)
Alert
4.1.2 基本控件开发简介
Screen共有4个子类:Alert Form List和TextBox。这四个子类本身就是屏幕类,可以直接在屏幕上显示。
Alert:用于显示提示信息的屏幕类。
List: 用来显示列表的屏幕类。
TextBox:用来输入文字信息。
一个复杂的程序界面往往需要很多类型的输入方式和显示方式。
Form类被设计成可以包含多个不同类型的控件,使用Form类可以管理屏幕上的Item控件,基本的Item控件有DateField TextField ImageItem Gauge ChoiceGroup StringItem等。
4.2 事件处理
Java程序中,实现与用户的交互功能都是需要通过事件来处理的,需要指定控件所使用的时间监听器。
事件就是用户与GUI(图形界面)交互时候所触发的事情。例如按键就是事件。
J2ME中,事件会被传送到事件处理器,一般由J2ME中的监听器来实现。监听器是能够检测并且能够相应事件的代码。
需要注意的是每个事件的处理总是在前一个事件处理完成以后程序才会接着处理,意味着事件不能够同时被处理,它们是串行处理的。
4.2.1Command编程基础
高级屏幕类TextBox、List和Form都可以在屏幕上添加Command(菜单项)。javax.microedition.lcdui.Command就是一般应用程序中使用的系统菜单中的菜单项的概念。由于Displayable对象中定义了addCommand()/removeCommand()两个方法,意味着Command类适用于高级API(Screen)与低级API(Canvas)。
API:
new Command(Labels, Type, Priority);
方法:
getLabel()
getLongLabel()
getCommandType()
getPriority()
第二个参数是Command的类型:共有八种类型。注意Command并不会因为设定成某一种类型就具有该类型“名称”的功能。例,某个Command不会因为设置为Command.EXIT,选择后就真的会终止程序。但每种类型都有其隐含的意义。
类型 默认数值 作用
BACK 2 逻辑上返回前一个屏幕
CANCEL 3 取消当前屏幕的操作和提示
EXIT 7 退出程序
HELP 5 请求帮助
ITEM 8 提示实现指定屏幕上的某一项
OK 4 肯定当前屏幕的操作或指示
SCREEN 1 应用到当前屏幕
STOP 6 停止当前运行的操作
在不同的机器上,Command的位置会因为类型不同而有所不同。有些机器会以抬头菜单的形式出现(Nokia 9210),有些机器会根据类型自动修改按钮在屏幕上的名称(Nokia 3300)。只能确定的是,Command会以以用户熟悉的操作方式来呈现。并根据类型,将Command放置到该设备最合适的位置上。
第三个参数是优先权,号码越低优先权越高,优先权越高,代表用户越能够方便的找到它们。数值相同时,使用方法本身默认的数值。
例:
Form f = new Form(“test”);
Command c = new Command(“menu1”, Command.Screen, 1);
f.addCommand(c );
display.setCurrent(f);
另外,Command类有另外构造方法,共4个参数:
Command(“short name”, “long name”, commandType, priority)
其中第二个参数是相对于第一个参数而来的。系统在屏幕上显示长名或者短名,不同产
家的实现不同。如果没有指定长名,就一定会使用短名。标准MIDP模拟器中,如果Command对应到软件按钮(例如:Command.Back),就会使用短名,如果用到菜单显示,就会自动使用长名。建议,尽量短名不超过四个字,长名在四个字以上。
练习:在屏幕上添加Command,要求使用Command的8种命令类型。
4.2.2通用事件处理CommandListener
Command必须和javax.microedition.lcdui.CommandListener事件处理接口一起使用才能反映用户的动作。Displayable类中定义了setCommandListener(CommandListener)方法,所以高级API和低级API都可以使用CommandListener。
使用步骤:
z implements CommandListener
z 添加菜单项 addCommand(Command)
z setCommandListener(CommandListener)
z 实现接口CommandListener的方法:
commandAction(Command c, Displayable s)
其中,c是被选定的系统菜单项的引用,s是发生事件的来源,也就是包含了此Command对象的Displayable实例。
完成上述步骤,在点选菜单项时,实现了CommandListener接口的类中的commandAction(Command, Displayable)方法就会被调用。这种向事件来源注册,然后等待通知的事件处理模型在Java中称为委托模型(Delegation Model)。
4.2.3 处理高级别事件
每一个高级界面的Displayable对象都有一个相应的监听器,该监听器用来监听控件是否已经被触发了相关的事件。
步骤同4.2.2。
练习:编写exitCommand,并实现按键后退出程序的功能。
4.3 Ticker类
是一个类似跑马灯的类,在所有的Displayable的子类上都可以加入Ticker。
API:
Ticker(String s)
方法:
setString(String s) 设置显示内容。
使用方法:
displayable.setTicker(ticker);
displayable.getTicker();
利用定义在Displayable中的setTicker()方法设定画面上的Ticker,或者getTicker()获取画面内包含的Ticker对象。
注意:MIDP1.0中Ticker只能用于Screen的子类。MIDP2.0可以用在Displayable子类了。
4.4 基本屏幕控件
屏幕控件就是屏幕显示的控件,目前主要有4种:
z List
z TextBox
z Alert
z Form
4.4.1 TextBox
API:
TextBox(String title, String text, int maxSize, int Constraints)
title:标题
text:初始化内容
maxSize:可输入的最大字符数
Constrains:输入限制
z TextField.ANY 允许输入任何字符或者数字
z TextField.EMAILADDR 允许输入电子邮件地址
z TextField.NUMERIC:整数 只允许输入数字,整型数
z TextField.PHONENUMBER 只允许输入电话号码的格式
z TextField.URL 允许用户输入URL形式的字符串
z TextField.DECIMAL 允许输入浮点型数
z TextField.PASSWORD 所有输入以星号表示
z TextField.UNEDITABLE 不允许修改
方法:
setString/getString() 设置/取得TextBox中的内容
setMaxSize()/getMaxSize() 设置/取得最大长度
size() 返回TextBox中文字的长度
delete() 删除TextBox实际内容
setChar()/getChar() 将TextBox存放的内容以一个char数组来存取
getCaretPosition() 取得目前输入光标所在位置
insert() 在特定位置加入内容
setConstraints(int constraints)设定输入限制
例:
TextBox tb = new TextBox(“number”, “123”, 5, TextField.NUMERIC);
注意:
设备一般都会自行限定TextBox所能存放的内容的最大值。构造方法中指定的最大值不能超过设备本身所限制的最大值。TextBox支持多行输入。
避免指定初始内容长度大于最大长度所引发的IllegalArgumentException。
练习:使用TextBox输入数据,按键后清空TextBox内容。
4.4.2 List
API:
List(String title, int listType)
List(String title, int listType, String[] stringElements, Image[] imageElements);
1. 单选方式Choice.Exclusive
2. 多选方式Choice.MULTPLE
3. 隐含方式Choice.IMPLICIT(简易式的单选)
Choice.EXCLUSIVE
Choice.IMPLICIT
Choice.MULTIPLE
比较三种不同的Choice型态
方法:
append(String, Image icon)若不需要图标,将第二项设置为null。
getSelectedIndex()
多选框的List事件,类似单选框的事件处理方法,不同的是需要通过getSelectedFlags(boolean[] arr)获得哪几个List的列表项被选择了,选择的结果放在一个布尔数组中。
getString(int index)获取指定索引的值。
insert()在指定项目后插入下一个项目。
set()重新设置某个项目的内容。
size()返回List中的项目数。
getImage()取得项目图标
getFont()/setFont()取得/设定系统显示项目使用的字体。
delete(int idx)删除特定选项
deleteAll()删除所有选项
isSelected(int idx)判断指定项目是否被选择。
(1)单选类型的List:
List list = new List("List test", Choice.EXCLUSIVE);
注意:Choice.EXCLUSIVE类型的List并不会在用户选择之后立刻引发事件,通常
通过系统菜单项来完成。
(2)多选类型的List
List list = new List("List test", Choice.MULTIPLE);
z 通过遍历List项目的方式,判断哪项被选中:
int size = l.size();
for (int i=0; i {
if (l.isSelected(i))
{
String value = l.getString(i);
System.out.println("第" + (i+1) + "项: " + value);
}
}
z 利用getSelectedFlags()方法传回boolean数组,判断哪项被选中。
setSelectedFlags()设定List之中的选项。
(3)简易式单选
List list = new List("List test", Choice.IMPLICIT);
与单选和多选不同,Choice.IMPLICIT类型的List会在用户选择之后立刻引发事件,并将List.SELECT_COMMAND通过commandAction()方法的第一个参数传入。判断commandAction()的第一个参数是否为List.SELECT_COMMAND,可以知道事件是否为List引发。因为这种类型的List同时间只有一个选项会被选择,所以我们可以利用List的getSelectedIndex()来判别是哪一个选项被选择。
例子:
public void commandAction(Command c, Displayable s)
{
if (c == List.SELECT_COMMAND)
{
List l = (List)s;
int index = l.getSelectedIndex();
String value = l.getString(index);
System.out.println("第" + (index+1) + "项: " + value);
}
}
4.4.3 AlertType
AlertType是一个工具类,不能实例化。但它提供了几种定义好的AlertType用以辅助Alert类的使用:
AlertType.ALARM 警报
AlertType.CONFIRMATION确认
AlertType.ERROR 错误
AlertType.INFO 提示信息
AlertType.WARNING 警告
AlertType提供的唯一的方法:
playSound(),可以用来发出几种类型的声音。
4.4.4 Alert
Alert是比较特殊的屏幕对象。利用Display显示于屏幕时将Alert显示于屏幕上,过一段时间后,自动调回之前的画面。
注意:将Alert显示之前,系统应存在一个画面,这样才能让Alert有地方可以跳回。因此,如果一启动就将Alert第一个显示于屏幕,会出现错误。
Alert的特性,使得它可以用作启动画面(Splash Screen)。
API:
Alert(String title)
Alert(String title, String alertText, Image alertImage, AlertType alertType)
方法:
setType()/getType() 设置获取Alert类型
setString()/getString() 设置/获取内含文字
setImage()/getImage() 设置/获取内含图像
setTimeOut() 设置显示时间
可以利用Alert类的setTimeOut()方法,传入Alert.Forever作为参数,此时Alert只有在用户按下解除按钮(Dismiss Command)时,才会跳回之前画面。
类型:
AlertType提供的几种类型。
Alert不允许使用Command对象
MIDP2.0新添加了setIndicator(Gauge indicator)方法。
4.4.5 Form与Item
Form是容器类型,可以配合Item类的子类产生丰富效果。
API:
Form(String title)
方法:
append(Item)
append(String)
set(int) 可以重新设定某个位置上的Item
get(int index) 可以取得指定位置Item的引用
size() 取得Form中Item的数量
getHeight()/getWidth() 可以返回Form用来摆放Item的区域的高和宽
delete(int pos) 删除指定Item
deleteAll() 删除Form上所有Item
Item
DateField
TextField
ImageItem
Gauge
ChoiceGroup
StringItem
CustomItem(MIDP2.0)
Spacer(MIDP2.)
注意:每一个Item子类同时只能属于一个容器,否则在加入其他容器时会产生IllegalStateException。如果某个Item想要加入其他容器中,必须在加入前从原来的容器中删除。
每个Form都被逻辑划分成多个区域,每个区域都有可能被一个以上Item占据。逻辑区域可能是自上而下划分,也有可能自左而右划分,取决于设备。例如,较长的屏幕,就有可能自上而下画出许多不等高的逻辑区域。
了解一下Item的布局方式:
z Item.LAYOUT_LEFT
z Item.LAYOUT_CENTER
z Item.LAYOUT_RIGHT
上述三种方式互斥,其他布局参考手册。
Item一般依照缺省布局绘制Item.LAYOUT_DEFAULT。可以利用setLayout()/getLayout()设置/取得目前布局。
尽管Form会被逻辑划分成多个区域,但并不是每个逻辑区域中只有一个Item。Form默认情况下,尽可能让不同Item出现在同一个逻辑区域中。
在下面情形下,Form将会自动将新的Item放到下一个逻辑区域中。
Item之后换行的情形:
1. StringItem内容后有换行符;
2. 具有LAYOUT_NEWLINE_AFTER设定的Item;
3. 如果是ChoiceGroup、DateField、Gauge、TextField,而且其布局并未设定为LAYOUT_2。
Item之前换行的情形:
1. 前一个Item之后有换行;
2. 具有LAYOUT_NEWLINE_BEFORE设定的Item;
3. StringItem内容开头有换行符;
4. 如果是ChoiceGroup、DateField、Gauge、TextField,而且其布局并未设定为LAYOUT_2;
5. Item布局设定为LAYOUT_LEFT、LAYOUT_CENTER、LAYOUT_RIGHT。
4.4.5.1 StringItem
在屏幕上显示只读信息的Item控件,用户不能更改控件的提示信息。可以按钮或者超级链接形式呈现。
API:
StringItem(String label, String text);
StringItem(String label, String text, int appearanceMode)
参数appearanceMode指定控件显示类型:
1. Item.PLAIN 默认类型
2. Item.HYPERLINK 超级链接
3. Item.BUTTON 图像按钮类型
方法:
getAppearanceMode() 取得StringItem所用的外观
getFont()/setFont() 用来取得/设定字体
getText()/setText() 用来取得/设定内容
getLable()/setLable 存取Label
在Form中加入StringItem,可以使用append(Item)方法。form.append(String)等同于调用:
form.append(new StringItem(null, String));
如果StringItem没有和Command关联起来,即使使用不同的外观设定,外观也是相同的。
4.4.5.2 Item与Command——ItemCommandListener
每个Item子类中,至少有三种东西:
1. Command数组;
2. 名为DefaultCommand的变量,引用到Command数组中的一个Command;
3. 一个指向ItemCommandListener的引用。
默认情况下,这些内部结构都是null。
addCommand()/removeCommand() 添加/删除Command
setDefaultCommand() 设定DefaultCommand
setItemCommandListener() 设定ItemCommandListener
使用removeCommand()删除的Command如果是DefaultCommand,那么系统会同时调用setDefaultCommand(null)。调用setDefaultCommand()时,如果传入的Command不在Command数组中,则系统会先调用addCommand()将其加入内部的Command数组中。自己调用setDefaultCommand(null),只会断开DefaultCommand与Command数组的引用,并不会将DefaultCommand所引用的Command从数组中删除。
Item一旦加入到Form中,用户选择到该Item时,那么该Item内部的Command数组就会变成系统菜单项。任何菜单项被选定之后,实现了ItemCommandListener接口的类中的commandAction()方法就会被调用(传入Command与Item的引用)。
StringItem一旦加入Command,其外观(Button、HYPERLINK)就会产生和PLAIN不同的样式。
设定Item的DefaultCommand的好处是,可以容易的让用户使用。用户只要直接在Item上按下按钮就可以产生默认执行效果,而不必通过选单选择后按确认再执行。DefaultCommand可以方便用户的使用。
基本使用步骤:
1) import javax.microedition.lcdui.*;
2) implements ItemCommandListener
3) 重写commandAction(Comand c, Item i)
4) displayable.addCommand()
5) displayable.setItemConmandListener()
4.4.5.3 ImageItem
图形显示控件,可以把图像作为Form的一个控件显示于屏幕。MIDP2.0中,增加了将图片作为按钮或者超级链接的功能。
API:
ImageItem(String label, Image img, int layout, String altText)
ImageItem(String label, Image img, int layout, String altText, int appearanceMode)
参数label:控件标题
参数layout:控件的布局
参数altText:如果不能够显示图片或图片不存在,则使用一个字符串替代图片
参数appearanceMode指定控件显示类型:
1. Item.PLAIN 默认类型
2. Item.HYPERLINK 超级链接
3. Item.BUTTON 图像按钮类型
方法:
setLayout()/getLayout() 设置/获取布局
setImage()/getImage() 设置/获取ImageItem中的Image
setAltText()/getAltText() 设置/获取取代文字
getAppearanceMode() 取得外观设定
Form的append(Image img)方法等同于:
form.append(new ImageItem(null, image, Item.LAYOUT_DEFAULT, null));
4.4.5.4 Spacer
用来在Form上加入一些空白间隔。功能特殊,不能与用户交互(不能使用Command)。
API:
Spacer(int minWidth, int minHeight)
4.4.5.5 ChoiceGroup
类似List。同List一样也实现了Choice接口。
API:
ChoiceGroup(String label, int choiceType);
ChoiceGroup(String label, int choiceType, String[] stringElements, Image[] imageElements)
参数choiceType指定控件的显示类型共3种:
z ChoiceEXCLUSIVE(单选)
z Choice.MULTIPLE(多选)
z Choice.POPUP(弹出式菜单)——MIDP2.0提供
注意,ChoiceGroup无法使用Choice.IMPLICIT类型。
ChoiceGroup与List的最大不同在于,ChoiceGroup必须在Form中才有用。
4.4.5.6 ItemStateListener
可以看到,Displayable的子类,可以使用addCommand()配合setCommandListener()做事件处理。Item类本身也有addCommand()方法,可以配合setItemCommandListener()做事件处理。
当Form组件内部的Item组件内部的状态发生改变时,Form组件会对所有使用setItemStateListener()向它注册的ItemStateListener的itemStateChanged()方法发出状态改变的消息。
以下几种情况会使得Form发出此消息:
1、 ChoiceGroup的状态改变了;
2、 Gauge的状态改变了
3、 TextField的状态改变了
4、 DateField的状态改变了
5、 Item组件的notifyStateChanged()被调用。
根据MIDP规范之中的定义,如果Form之中同时有CommandListener与ItemStateListener的话,那么itemStateChanged()会比commandAction()还要先被调用。
注意:ItemStateListener只有在与用户交互时,组件的状态真的被改变时,其itemStateChanged()方法才会被调用。如果是程序修改组件的状态,则不会触发该事件。除非在改变完某个组件状态后,调用notifyStateChanged()。
使用步骤:
1) import javax.microedition.lcdui.*
2) implements ItemStateListener
3) 重写itemStateChanged(Item i)
4) item.setItemStateListener(itemStateListener)
示例:
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
/**
*
* @author Allan
* @version
*/
public class ItemStateListenerTextMidlet extends MIDlet
implements ItemStateListener
{
private Display display;
private Image img1;
private Image img2;
private ChoiceGroup cg1;
private ChoiceGroup cg2;
public ItemStateListenerTextMidlet()
{
display = Display.getDisplay(this);
try
{
img1 = Image.createImage("/f1.png");
img2 = Image.createImage("/f2.png");
}
catch (Exception e)
{
}
}
public void startApp()
{
Form f = new Form("ChoiceGroup test");
cg1 = new ChoiceGroup("名称", Choice.EXCLUSIVE);
cg1.append("水果A", img1);
cg1.append("水果B", img2);
cg2 = new ChoiceGroup("价格", Choice.MULTIPLE);
cg2.append("10.2", img1);
cg2.append("0.85", img2);
f.setItemStateListener(this);
f.append(cg1);
f.append(cg2);
display.setCurrent(f);
}
public void pauseApp()
{
}
public void destroyApp(boolean unconditional)
{
}
public void itemStateChanged(Item item)
{
String label = item.getLabel();
if (label.equals("名称"))
{
System.out.println("名称的状态改变了");
int i = cg1.getSelectedIndex();
System.out.println(cg1.getString(i));
}
else if (label.equals("价格"))
{
System.out.println("价格的状态改变了");
boolean[] c = new boolean[cg2.size()];
cg2.getSelectedFlags(c);
for (int i=0; i {
if (cg2.isSelected(i))
{
System.out.println("" + cg2.getString(i));
}
}
}
}
}
4.4.5.7 TextField
同前面讲过的TextBox,请参考前面关于TextBox的使用。
示例:
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
/**
*
* @author Allan
* @version
*/
public class TextFieldTestMidlet extends MIDlet implements ItemStateListener
{
private Display display;
private TextField uid;
private TextField name;
private TextField password;
private TextField summary;
public TextFieldTestMidlet()
{
display = Display.getDisplay(this);
}
public void startApp()
{
Form f = new Form("TextField test");
uid = new TextField("UID", "", 5, TextField.NUMERIC);
name = new TextField("姓名","", 12, TextField.ANY);
password = new TextField("密码", "", 8, TextField.PASSWORD);
summary = new TextField("结果", "", 30, TextField.ANY);
f.append(uid);
f.append(name);
f.append(password);
f.append(summary);
f.setItemStateListener(this);
display.setCurrent(f);
}
public void pauseApp()
{
}
public void destroyApp(boolean unconditional)
{
}
public void itemStateChanged(Item i)
{
if (i == name)
{
summary.setString("您输入的用户名为:" + name.getString());
}
}
}
4.4.5.8 Gauge
一般让用户做大小的调整,或是用作进度条使用。
API:
Gauge(String label, boolean interactive, int maxValue, int initialValue);
参数interactive指定控件的类型:
z 交互类型(interactive设为true),可以通过手机键盘控制进度条进度;
z 非交互类型(interactive设为false),只能通过程序控制进度条。
参数maxValue指定控件最大数值
参数initialValue指明初始数值。
方法:
setMaxValue()/getMaxValue() 设定/取得允许的最大值
setValue()/getValue() 设定/取得初始值
isInteractive() 判断Gauge是否可与用户交互
规范中还定义了一种无范围Gauge。要求:
Gauge属于非交互类型(第二个参数设置为false),第三个参数传入Gauge.INDEFINITE就可以产生没有范围的Gauge。此时,第四个参数只能有四种选择:
z Gauge.CONTINUOUS_IDLE 目前程序停止
z Gauge.CONTINUOUS_RUNNING 以动画显示
z Gauge.INCREMENTAL_IDLE 目前程序停止,所以是一个静态的图
z Gauge.INCREMENTAL_UPDATING 以动画显示,只有每次重新调用setValue()画面才会慢慢更新。
示例一:
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
/**
*
* @author Allan
* @version
*/
public class GaugeTestMidlet extends MIDlet implements ItemStateListener
{
private Display display;
private Gauge g1;
private Gauge g2;
public GaugeTestMidlet()
{
display = Display.getDisplay(this);
}
public void startApp()
{
Form f = new Form("Gauge test");
g1 = new Gauge("进度条一", true, 10, 2);
g2 = new Gauge("进度条二", false, 10, 4);
f.append(g1);
f.append(g2);
f.setItemStateListener(this);
display.setCurrent(f);
}
public void pauseApp()
{
}
public void destroyApp(boolean unconditional)
{
}
public void itemStateChanged(Item i)
{
if (i == g1)
{
g2.setValue(g1.getValue());
}
}
}
示例二:
/*
* GaugeTestMidlet.java
*
* Created on 2006年2月23日, 下午3:30
*/
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
/**
*
* @author Allan
* @version
*/
public class GaugleIndefiniteMidlet extends MIDlet implements CommandListener
{
private Display display;
private Gauge g1;
private Command c1;
private Command c2;
private Command c3;
private Command c4;
public GaugleIndefiniteMidlet()
{
display = Display.getDisplay(this);
c1 = new Command("CONTINUOUS_IDLE", Command.SCREEN, 1);
c2 = new Command("CONTINUOUS_RUNNING", Command.SCREEN, 1);
c3 = new Command("INCREMENTAL_IDLE", Command.SCREEN, 1);
c4 = new Command("INCREMENTAL_UPDATING", Command.SCREEN, 1);
}
public void startApp()
{
Form f = new Form("Gauge test");
f.addCommand(c1);
f.addCommand(c2);
f.addCommand(c3);
f.addCommand(c4);
g1 = new Gauge("进度条一", false, Gauge.INDEFINITE, Gauge.CONTINUOUS_IDLE);
//g1 = new Gauge("进度条一", false, Gauge.INDEFINITE, Gauge.CONTINUOUS_RUNNING);
//g1 = new Gauge("进度条一", false, Gauge.INDEFINITE, Gauge.INCREMENTAL_IDLE);
//g1 = new Gauge("进度条一", false, Gauge.INDEFINITE, Gauge.INCREMENTAL_UPDATING);
//f.setCommandListener(this);
f.append(g1);
display.setCurrent(f);
}
public void pauseApp()
{
}
public void destroyApp(boolean unconditional)
{
}
public void commandAction(Command c, Displayable s)
{
if (c == c1)
{
g1.setValue(Gauge.CONTINUOUS_IDLE);
}
else if (c == c2)
{
g1.setValue(Gauge.CONTINUOUS_RUNNING);
}
else if (c == c3)
{
g1.setValue(Gauge.INCREMENTAL_IDLE);
}
else if (c == c4)
{
g1.setValue(Gauge.INCREMENTAL_UPDATING);
}
}
}
4.4.5.9 DateField
可以让用户方便地输入日期和时间。
API:
DateField(String label, int mode)
DateField(String label, int mode, TimeZone timeZone)
参数mode指定控件类型(3种):
z DateField.DATE 输入日期
z DateField.TIME 输入时间
z DateField.DATE_TIME 输入日期时间
方法:
setInputMode()/getInputMode() 设置/取得输入模式
setDate()/getDate() 设置/取得时间(Dateu需要和Calendar类配合)
示例:
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.*;
/**
*
* @author Allan
* @version
*/
public class DateFieldTestMidlet extends MIDlet
{
private Display display;
private DateField df1;
private DateField df2;
private DateField df3;
public DateFieldTestMidlet()
{
display = Display.getDisplay(this);
}
public void startApp()
{
Form f = new Form("DateField test");
TimeZone tz = TimeZone.getDefault();
System.out.println("系统默认时区是:" + tz.getID());
Date now = new Date();
df1 = new DateField("日期", DateField.DATE);
df1.setDate(now);
df2 = new DateField("日期", DateField.TIME, tz);
df2.setDate(now);
df3 = new DateField("日期", DateField.DATE_TIME, TimeZone.getTimeZone("CCT"));
df3.setDate(now);
f.append(df1);
f.append(df2);
f.append(df3);
display.setCurrentItem(df1);
}
public void pauseApp()
{
}
public void destroyApp(boolean unconditional)
{
}
}
4.4.5.9 Alert与Gauge
利用Alert的方法setIndicator()/getIndicator(),可以将Alert当作显示进度的画面。用Gauge做进度显示,但它必须满足以下条件:
1) 不能与用户交互,即第二个参数为false.
2) 不能已经被其他Form或是Alert使用(Item的子类同时间只能属于一个容器)
3) 不能利用addCommand加入任何Command
4) 不能有ItemCommandListener
5) 不能有Label,即构造函数的第一个参数必须给定null
6) 不能使用setLayout()指定Item.LAYOUT_DEFAULT以外的layout
7) 不能使用setPreferSize()指定期望的组件尺寸
示例:
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
/**
*
* @author Allan
* @version
*/
public class AlertWithIndicatorMidlet extends MIDlet
implements CommandListener, Runnable
{
private Display display;
private Gauge g;
private boolean exit = false;
public AlertWithIndicatorMidlet()
{
display = Display.getDisplay(this);
}
public void startApp()
{
Alert a = new Alert("处理中");
a.setType(AlertType.INFO);
a.setTimeout(Alert.FOREVER);
a.setString("系统正在处理中...");
g = new Gauge(null, false, 10, 0);
a.setIndicator(g);
Command start = new Command("Start", Command.OK, 1);
Command stop = new Command("Stop", Command.STOP, 1);
a.addCommand(start);
a.addCommand(stop);
a.setCommandListener(this);
display.setCurrent(a);
System.out.println(Thread.currentThread().getName());
}
public void pauseApp()
{
}
public void destroyApp(boolean unconditional)
{
}
public void run()
{
if (!exit)
{
for (int i=0; i<11; i++)
{
g.setValue(i);
System.out.println(Thread.currentThread().getName());
try
{
Thread.sleep(1000);
}
catch (Exception e)
{
}
}
}
}
public void commandAction(Command c, Displayable s)
{
System.out.println(Thread.currentThread().getName());
String cmd = c.getLabel();
if (cmd.equals("Start"))
{
new Thread(this).start();
}
else if (cmd.equals("Stop"))
{
exit = true;
destroyApp(false);
notifyDestroyed();
}
}
}
注意:所有UI相关的回调函数都是由同一个Thread完成,具有依序执行的特性。