J2ME学习笔记
|
|
Author: Wupei | Date: 2007-08-08 |
View: 7808 |
J2ME开发 - 新手上路 | Digg:
4
|
|
先从最基本的开始说起吧~ 1.Optional Package 先从最下层的Configuration开始说起,它是J2ME规定嵌入式设备能够执行Java程序的规范,J2ME把运算功能有限,电力有限这类的嵌入式设备叫做Connected Limited Device,规定这些设备如果能执行JAVA程序,则必须遵守哪些规范,而这些规范就定义在Connected Limited Device Configuration里面,简称CLDC,类似有个可连接,资源相对来说无限的设备则被称为Connected Device,同样有个CDC规范。我们平时所使用的手机属于Connected Limited Device范畴,支持J2ME的手机必须支持支持CLDC规范。就目前来看 CLDC有1.0和1.1两种,两者最大的不同则是后者支持浮点运算,而前者不支持,CDC1.0和CDC1.1的核心类库(java.lang.*,java.io.*,java.util.*)和J2SE几乎没什么区别,CLDC1.0则差别较大,CLDC1.1相对来说增强了CLDC1.0的功能,最小内存数量也从160K提升到 192K。CLDC加入的支持扩展类库为有javax.microedition.io.*等。 通过查阅CLDC的类库文档 可以得到以下一些信息,为了方便大家做个了解,我把几个大类做了一下整理: 系统类: java.lang.Object java.lang.Class java.lang.Runtime java.lang.System java.lang.Thread java.lang.Runnable(接口) java.lang.String java.lang.StringBuffer java.lang.Throwable 数据类型类: java.lang.Boolean java.lang.Byte java.lang.Short java.lang.Integer java.lang.Long java.lang.Float(从1.1版本开始支持) java.lang.Double(从1.1版本开始支持) java.lang.Character 集合容器类: java.util.Vector java.util.Stack java.util.Hashable java.util.Enumeration(接口) 输入/输出类: java.io.InputStream java.io.OutputStream java.io.ByteArrayInputStream java.io.ByteArrayOutputStream java.io.DataInput(接口) java.io.DataOutput(接口) java.io.DataOutputSteam java.io.DataInputStream java.io.Reader java.io.Writer java.io.InputSteamReader java.io.OutputStreamReader java.io.PrintStream 日历和时间类: java.util.Calendar java.util.Date java.util.TimeZone 其他附加类: java.util.Random java.lang.Math 错误类: java.lang.Error java.lang.NoClassDeFoundError(从1.1版本开始支持) java.lang.OutOfMemoryError java.lang.VirtualMachineError 异常类: java.lang.Exception java.lang.ArithmeticException java.lang.ArrayIndexOutOfBoundsException java.lang.ArrayStoreException java.lang.ClassCastException java.lang.ClassNotFoundException java.lang.IllegalAccessException java.lang.IllegalArgumentException java.lang.IllegalMonitorStateException java.lang.IllegalThreadStateException java.lang.IndexOutOfBoundsException java.lang.InstantiationException java.lang.InterruptedException java.lang.NegativeArraySizeException java.lang.NullPointerException java.lang.RuntimeException java.lang.NumberFormatException java.lang.SecurityException java.lang.StringIndexOutOfBoundsException java.util.EmptyStackException java.util.EmptyNoSuchElementException java.io.EOFException java.io.InterruptedIOException java.io.IOException java.io.UnsupportedEncodingException java.io.UTFDataFormatException 弱引用:(从1.1版本开始支持) java.lang.ref.Reference java.lang.ref.WeakReference 以下为CLDC1.1扩展类库: javax.microedition.midlet.* javax.microedition.lcdui.* javax.microedition..rms.* javax.microedition..io.* javax.microedition.lcdui.game.* javax.microedition.pki.* javax.microedition..media.* javax.microedition.media.control.* 下面来说说Profile,既然Profile构建在Configuration之上,其所规范的配备需求自然不会比Configuration低,而我们通常所提到的Profile有Mobile Information Device Profile(简称MIDP),针对的是手机和寻呼机,等移动设备。还有一种常见的Profile,Information Module Profile,是针对没有屏幕的移动设备所设计的。 目前MIDP,最高的规格是2.0,硬件要求为: 内存:256Kb非易失性内存可供MIDP组件使用,8Kb非易失性内存可工应用程序创建持久化数据,128Kb易失性内存可供虚拟机运行时环境使用。 显示:屏幕大小96*54,显示深度:1位,象素形状(长宽比):1:1。 输入:单手键盘或者双手键盘或者触摸屏。 网络:双向,无线,可以是间歇的,带宽有限的。 声音:播放不同声调的能力,可以通过专用硬件也可以通过软件算法获得。 J2ME规范中还定义了厂商可以选择实现的包,那就是Optional Packages,通常不同的厂商不同的产品会有一些不同的功能,那么制造厂商可以通过Optioanl Packages规范来为实现某些产品的特殊功能制定相应的API。 那我们通常所说的J2ME开发,大部分其实都是MIDP程序设计,那我们先来看几个名词的解释: MIDlet:一个可执行的应用手机程序的基本单位。 MIDlet Suite:许多MIDlet所构成的集合一般称做MIDP Application(MIDP应用程序)。 JAR文件(JAR File,.jar文件) 实际是包裹着MIDlet Suite的文件,属于ZIP压缩格式。 JAD文件(Descriptor File,.jad文件) 用来描述MIDlet Suite的基本信息的文本文件,它是一个外部文件(不存在于JAR文件内部,独立存在的文件)。
下图是LCDUI包的简要结构: 在MIDP中,和用户界面相关的回调函数有四个: (1) javax.microedition.lcdui.Command类; (2) javax.microedition.lcdui.Canvas类 (3) 在屏幕重绘时,会产生重绘事件,此时的Canvas类的paint()方法会被调用,并传入一个Graphics对象的调用; (4) 调用Display类的callSerially()方法时,会传入一个Runnable接口的类,其中的run()方法会被调用。 下面我们来介绍一下javax.microedition.lcdui.Command这个类,它既适用于高级API(Screen类),又使用于低级API(Canvas类). Command类有两种构造函数,一个有三个参数,另一个则是四个参数。 区别在于:前者第一个参数是显示在画面上名称,第二个参数是命令的类型,第三参数是优先权,以自然数形式表示,如:1,2,3…,越小优先权越高,代表在屏幕显眼的地方显示。而后者则是在前者的基础上增加了一个长命令的参数,具体位于前者第一和第二个参数之间,具体机型上的显示和厂家的实现的不同而不同。 前面介绍的命令的类型共分为8种:Command.BACK,ommand.EXIT,Command.CANCEL,Command.HELP, Command.ITEM,Command.OK,Command.SCREEN,Command.STOP。 例: Command c = new Command("取消", Command.CANCEL, 1); Command a = new Command("这是长命令", "这是短命令", Command.BACK, 1);在屏幕上光显示出这些是没有任何意义的,我们必须还要将Command类和javax.microedition.lcdui.CommandListener结合起来使用才能反应用户的动作,由于setCommandListener()定义于Displayable中,所以CommandListener是一个和Command一样可以通用与高级API和低级API的事件处理接口。 需要注意的是CommandListener采用的是Unicast机制,也就是说,同一时刻只能注册一个时间处理函数。 下面是一个在MIDlet中使用Command和CommandListener的大致情形: public class example1 extends MIDlet implements CommandListener { public void startApp() { //……… Command exp1 = new Command("命令1", Command.OK, 1); //……… } public void pauseApp() { //……… } public void destroyApp(boolean exp) { //……… } public void commandAction(Command c, Displayable s) { //……… } } 介绍完了Command类,我们来介绍一下Ticker类,在MIDP1.0中,它只能用于Screen的子类,在MIDP2.0后,已经可以用于Displayable的所有子类了。它是一个类似于跑马灯的类,即通常所见的进度条,我们通常用setTicker()来设定画面上的Ticker,或者用getTicker()来取得画面上的Ticker对象,setTicker()中的括号内的双引号中的字符串即显示在跑马灯中的文字。 下面用一个例子来详细介绍一下上面几个类的用法,作为本章的小结。 import javax.microedition.lcdui.*; import javax.microedition.midlet.*; public class DisplayableTestMIDlet extends MIDlet implements CommandListener { public void startApp() { display = Display.getDisplay(this); } public void pauseApp() { f = new Form("Displayable测试"); Command test1 = new Command("开始Ticker", Command.SCREEN, 1); Command test2 = new Command("停止Ticker", Command.SCREEN, 1); Command test3 = new Command("退出", Command.EXIT, 1); f.addCommand(test1); f.addCommand(test2); f.addCommand(test3); f.setCommandListener(this); display.setCurrent(f); } public void destroyApp(boolean unconditional) { } public void commandAction(Command c, Displayable s) { String cmd = c.getLabel(); if (cmd.equals("开始Ticker")) { f.setTicker(new Ticker("运作中")); } if (cmd.equals("停止Ticker")) { f.setTicker(null); } } private Display display; Form f; } Screen类有四个相关的子类,分别为Alert,List,TextBox,Form,这四个子类可以细分为两类,前三者属于封装了较复杂用户界面的类,我们只能单纯的拿来使用,对于其内部的组成结构无法修改;相对前三者,Form则是自由的多,它是默认缺省没有任何用户界面的组件,它类似于一个容器,可以容纳Item类的子类,以构成复杂的图形用户界面.
代码示范如下: import javax.microedition.lcdui.*; import javax.microedition.midlet.*; public class ImplicitListWithFitPolicyMIDlet extends MIDlet implements CommandListener { private Display display; public ImplicitListWithFitPolicyMIDlet() { display = Display.getDisplay(this); } public void startApp() { Image img = null; try { img = Image.createImage("/A.png"); } catch (Exception e) { System.out.println(e); } List l = new List("List测试", Choice.IMPLICIT); System.out.println("缺省的Fit Policy:" + l.getFitPolicy()); l.append("12345678901234567890", img); l.append("abcdefgabcdefg", img); Command c1 = new Command("WRAP_OFF", Command.OK, 1); l.addCommand(c1); Command c2 = new Command("WRAP_ON", Command.OK, 1); l.addCommand(c2); Command c3 = new Command("WRAP_DEFAULT", Command.OK, 1); l.addCommand(c3); l.setCommandListener(this); display.setCurrent(l); } public void commandAction(Command c, Displayable s) { String cmd = c.getLabel(); if (cmd.equals("WRAP_OFF")) { List tmp = (List) s; tmp.setFitPolicy(Choice.TEXT_WRAP_OFF); } if (cmd.equals("WRAP_ON")) { List tmp = (List) s; tmp.setFitPolicy(Choice.TEXT_WRAP_ON); } if (cmd.equals("WRAP_DEFAULT")) { List tmp = (List) s; tmp.setFitPolicy(Choice.TEXT_WRAP_DEFAULT); } } public void pauseApp() { } public void destroyApp(boolean unconditional) { } } 下面我们再来说一说TextBox,当用户需要输入文字时,TextBox就派上用场了,TextBox定义使用如下:TextBox tb=new TextBox(“文字输入测试”,”名字”,9,TextField.ANY); 其中’输入测试’是Title,而’名字’则是我们指的内容,第三个参数就是我们要限制的参数2的字符数,第四个参数则是对输入内容做了更进一步的限制,ANY指的是允许输入任何字符或数字,类似还有EMAILADDR则是指允许输入电子邮件地址,等等(参见API说明文档)。 下面介绍一下AlertType,它是一个工具类,本身是无法实体化的它提供了几种定义好的AlertType用以辅助Alert类的使用。比如:ALARM(警报),CONFIRMATION(确定),ERROR(错误)等等,Alert是一个比较特殊的Screen类的对象,当我们利用Display.setCurrent()这个方法的时候,它会先发出一段声音,然后将自己显示在屏幕上,过一段时间后,跳回之前的画面,因此,请注意,我们必须要使系统一定要存在一个画面,这样才能让Alert条回,否则,会发生错误,下面一段代码提示Alert的使用: import javax.microedition.lcdui.*; import javax.microedition.midlet.*; public class AlertSplashScreenMIDlet extends MIDlet { private Display display; public AlertSplashScreenMIDlet() { display = Display.getDisplay(this); } public void startApp() { Alert a1 = new Alert("应用程序开始启动"); a1.setType(AlertType.INFO); setTimeout(5000); a1.setString("应用程序初始化中 ,请稍等"); Form f = new Form("主画面"); dsplay.setCurrent(a1, f); } public void pauseApp() { } public void destroyApp(boolean unconditional) { } } 至于Form,是这个Screen类中最终要的一个子类,是高级图形界面中最长用到的 当我们选择了Canvas这个类,就意味着我们准备采用低级API,之所以称之为低级API,并不是因为它很低能,恰恰相反,我们可用它来处理一些很底层的系统事件,具体如何支持,我们要看硬件上的支持程度来判定。 所有的低级API都是从javax.microedition.lcdui.Canvas开始,我们必须继承Canvas,才可以建立一个显示在屏幕上的画面,需要注意的是每一个Canvas里面必须有一个抽象方法paint(),并且需要传入一个Graphics的引用,可以说Canvas的精华就在这个paint()方法里面,我们用这个方法来负责这个屏幕画面的描绘。
这是我们在绘制图象时要注意的。
下面我用一段简单的代码来说明一下这个Graphics对象的应用: import javax.microedition.lcdui.*; import javax.microedition.midlet.*; public class test extends Canvas { public void paint(Graphics g) { g.setColor(255, 255, 0); g.fillRect(0, 0, getWidth(), getHeight()); int c = g.getColor(); int dc = g.getDisplayColor(g.getColor()); System.out.println("当前画面的颜色为:" + Integer.toHexString(c)); System.out.println("当前画面的R值为:" + g.getRedComponent()); System.out.println("当前画面的G值为:" + g.getGreenComponent()); System.out.println("当前画面的B值为:" + g.getBlueComponent()); System.out.println("当前画面的显示颜色为:" + Integer.toHexString(dc)); System.out.println("当前画面的灰度为:" + g.getGrayScale()); } } 需要大家注意的是R,G,B的值只能在0——255之间,不可以超出这个范围,另外我们可以直接用0x00RRGGBB格式进行颜色的调配。 上面的代码简要说了一下Graphics几个重要的参数如何获得,下面我们就来谈一谈如何用Graphics做具体的图形,如果我们需要绘制一条直线,我们可以调用drawLine()方法,需要定义其开始坐标和结束坐标,共四个参数,同时,Graphics提供两种形式的线条,一个是虚线,即Graphics.DOTTED,一个是实线,即Graphics.SOLID,同样我们给出一段代码供大家参考: import javax.microedition.lcdui.*; import javax.microedition.midlet.*; public class GraphicsTestCanvas2 extends Canvas { public void paint(Graphics g) { g.setColor(255, 255, 255); g.fillRect(0, 0, getWidth(), getHeight()); g.setColor(255, 0, 0); g.drawLine(1, 1, 100, 10); g.setStrokeStyle(Graphics.DOTTED); } } 用类似的方法,我们可以实现用Graphics的drawRect()和drawRoundRect()方法来绘制矩形和圆角矩形,具体的方法应用,大家可以见WTK包的说明文档。 下面我们来说一说Canvas的屏幕事件处理。 Canvas本身有两种状态,一种是普通默认情况下的,一种是全屏状态,可以用setFullScreenMode()方法来对其设定,两者之间的区别在于当我们使用全屏幕状态的时候,Title,Ticker,以及我们的Command都无法在屏幕上显示,而且当我们调用setFullScreenMode()的时候,不管是什么模式,都会调用seizeChanged()这个方法,并传入屏幕的高度和宽度作为其参数,对于某些突发事件,比如说来电等等,屏幕会被系统画面所覆盖的时候,就会调用hideNotify()这个方法,当恢复原状时,就会调用我们原本的画面,那么系统就会同时调用showNotify()这个方法。 在Canvas里面我们每按下一个按键都会触发keyPressed()函数,并传入相应位置的整数值,我们在MIDP规范中可以很容易的发现,KEY_NUM0——KEY——NUM9十个常数分别代表键盘上的0-9,还有两个功能键,KEY_STAR,KEY_POUND,如果我们传入的值小于0,代表我们传入了不合法的keycode,某些机器上还支持连续按键响应,但这并不是JTWI规定要支持的,所以我们在进行实际开发之前一定要用我们前面讲到的hasRepeatEvents()方法来进行判定。 在索爱P910C这样的高端手机上,还支持屏幕的触控事件,同样这也不是JTWI做了硬性规定,所以我们实际开发中也要进行测试,我们在屏幕上点击,可以引发pointerPressed()函数,并传入当时位置的坐标,放开后,会引发pointerReleased()函数,同样也会传入坐标,具体的使用方法和keyPressed()以及keyReleased()大同小异,大家可以参考一下WTK的说明文档获得比较详细的方法使用规则 更多阅读: |
|
| 最近更新 ( 2010-04-05 ) |
尚无评论发表