♦ 介绍
在上一讲中讲述了如何利用高级API制作简单的应用程序。在这讲中,我们将讲述如何利用低级API进行图解应用程序的开发。
♦ 低级API
在上一讲中所叙述的高级API具有很高的移植性,由于不能仅限于使用被定义的部分,因此,制作成具有很大的制约限制的UI是相当困难的。但是,在本讲中将要学习的低级API,由于不同机种的显示屏的尺寸和按键的数量也不一样,所以即使移植性降低,也能完全按照自己的意图制作UI,还可以直接访问输入装置。
Canvas类和Graphics类实现了低级API。Canvas类是Displayable类的子类、用于图形和事件操作。一个canvas组件表示一个可绘画的屏幕区域,一个Grafics类为描绘Canvas组件提供图片,文本和形状。
♦ 3-1 Canvas类
Canvas类是Displayable的子类,提供描绘和处理低级事件的方法。另外,Canvas类是抽象类,因此必须在应用中创建继承Canvas类的单独的类。
Canvas类中有一个名为paint(Graphics g)的抽象方法。用paint方法中去实现需要的图片,文本以及图形。所以我们要跳过这个方式来讲解。
Code Example 1
运用Canvas类,在paint(Graphics)方法中,可以进行图片和文本的制作。
Class MyCanvas extends Canvas{ void paint(Graphics g){ . . . } } |
♦ 3-1-1 获取手机终端的画面尺寸
与PC画面相比,手机终端的画面尺寸较小,有必要在其中创建用户接口,所以设置是非常重要的。为此,使用Canvas描绘画像、TEXT、图形时有必要知道画面的具体尺寸。根据手机终端不同,能够在画面上描绘的范围大小也各不相同,在Canvas类中有获取画面尺寸的方法:
• int getWidth()
• int getWidth()
不用预先知道具体硬件画面的尺寸,使用getWidth(),getHeight()灵活地获取,具有较高重用性。
♦ 3-2 Graphics类
Graphics类具有2D的绘画功能、与J2SE的java.awt.Graphics非常的相似。定义了描绘TEXT、画像、图形等等各种各样的方法。
♦ 3-2-1 图形的描绘
♦ 3-2-1-1 线的描绘
(x1,y1)是线的起点;(x2,y2)是线的终点。
能够用void drawLine(int x1,int y1,int x2, int y2); 描绘线。
♦ 3-2-1-2 四边形的描绘
使用以下的方式进行四边形的描绘。此时的(x,y)被指定为想要描绘的四边形左上方顶点的坐标。
• void drawRect(int x,int y,int width,int height)
• void drawRect(int x,int y,int width,int height)
用drawRect方法描绘四边形。用fillRect描绘中间全被涂抹的四边形。另外,Graphics图形的描绘方式中有draw的方法和fill的方法。draw只是单指图形的描画。而fill~则是指中间被涂抹的图形的描画。
此外,利用下面的方法,能够描绘出圆角的四角形。
• void drawRoundRect(int x,int y,int width,int height.int arcWidth,int arcHeight)
• void fill RoundRect(int x,int y,int width,int height.int arcWidth,int arcHeight)
图1所示,是方法中参数的定义和描述。图2中是示例。
![]() |
Figure 1 |
![]() |
Figure 2 |
♦ 3-2-1-3 圆的描绘
利用以下方式进行圆的描绘。
• void drawArc(int x,int y,int width,int height,int startAngle,int arcAngle)
• void fillArc(int x,int y,int width,int height,int startAngle,int arcAngle)
此时,坐标(x,y)是高为height、宽为width的长方形左上方角的顶点坐标。若是height和width的值相等则可以描绘出圆的弧线。startAngle,arcAngle分别设定各自的角度(单位:°)。图3所示的是各自的变量;图4所示的是表示例。
![]() |
Figure 3 |
![]() |
Figure 4 |
♦ 3-2-1-4 色彩的描绘
运用Graphics类指定颜色给描绘的图形和文本。如果先定义好颜色,那就能按照预先的设定颜色显示。用如下方法进行颜色设定。
• void setColor(int RGB)
• void setColor(int red, int green , int blue)
• void setGrayScale(int value)
用16进制指定色彩的情况下,则要在RGB中指定16进制的数值。此时要显示16进制的状况就要在数头附加「0x」。用10进制指定的情况下,分别用3个不同的[0-255]的数值指定red,green,blue。使用标尺的情况下,在10进制[0-255]的范围中指定色彩的浓淡。
若要更改北景的色彩时,首先在setColor方式中指定色彩,随后在fillRect方式中描绘填充的四边形。
//背景呈黑色 g.setColor(0x000000); g.fillRect(0,0,getWidth(),getHeight()); |
根据以下的方法能够获取现在已经设定的色彩值。
• int getColor()
• int getBlueComponent()
• int getGreenComponent()
• int getRedComponent()
• int getGrayScale()
getColor方法下能够获取设定完成的RGB16进制数值。getBlueComponent,getGreenComponent,getRedComponent下的青、蓝、红的数值能够获取在10进制的[0-255]的范围内的数值。
♦ 3-2-1-5 线型
Graphics类中准备了描绘线时的2个式样:DOTTED和SOLID。draw方法下,线型的指定是有效的。fill方法中线型的设定是无效的。 利用以下方法进行设定。
• void setStrokeStyle(int style)
线型的样式中有DOTTED(点线)和SOLID(实线),若要指定DOTTED时, Graphics类的参数就是Graphics.DOTTED,指定SOLID时,Graphics类的参数就是Graphics.SOLID。(图5)
![]() |
Figure 5 |
♦ 3-2-2 Text的描绘
使用以下方法描绘Canvas上的文本
• void drawChar(char character,int x,int y,int anchor)
• void drawChar(char[] data,int offset,int length,int x,int y,int anchor)
• void drawString(String string,int x,int y,int anchor)
• void drawSubString(String str,int offset,int length,int x,int y,int anchor)
♦ 3-2-2-1 TEXT字体的设定
在应用中描绘TEXT时,并不只是默认的文字,应该也有必要描绘粗体字和下划线文字吧?MIDP的API中有表示字体的Font类。和Graphics的对象一样,可以定义一个Font的对象去更改文本的字体。按照以下的方法获取Font对象:
• static Font getFont(int face,int style,int size)
Font中有face,style,size3个属性。以下图表中表示的是能够指定的值。
属性 | Face | Style | Size |
能够指定的值 | FACE_MONOSPACE FACE_PROPORTIONAL | STYLE_BOLD STYLE_ITALIC | SIZE_LARGESIZE_MEDIUM SIZE_SMALL |
FACE_SYSTEM | STYLE_PLAIN STYLE_UNDERLINED | SIZE_SMALL |
运用逻辑操作符号“|“可以同时使用多个属性。
为使获取的Font能在Graphics中设定,则要使用
• void setFont(Font font)
请看下面的范例。
Font font=Font.getFont(Font.FACE_SYSTEM,Font.STYLE_ITALIC|Font.STYLE_BOLD, Font.SIZE_LARGE); g.setFont(font); |
Code Example 2
另外,请按照如下方式获取字体的各种信息:
• int charsWidth(char[] ch,int offset,int length)
• int charWidth(char ch)
• int getBaselinePosition()
• int getHeight()
• int stringWidth(String str)
• int substringWidth(String str,int offset,int len)
♦ 3-2-2-2 定位点
像Text和画像那样在Canvas上配置子类时位置决定是必要的情况下,指定被称为定位点的基点,将定位点作为描绘的基准进行描绘。
在定位点中有如下的内容。
Horizontal | Vertical |
LEFT HCENTER RIGHT | TOP BASELINE VCENTER BOTTOM |
定义定位点时,使用使用逻辑运算符[|],有必要分别指定横方向和纵方向。其中能够在画像描绘的定位点上指定垂直方向,但是应该注意,在Text描绘的定位点上不能够指定垂直方向。
drawString("String",x,y,width,height, Graphics.LEFT | Graphics.TOP); |
Code Example 3
如上所示指定的定位点的坐标和文字关系显示状况如下图所示。
![]() |
Figure 6 |
另外,按照如下所示的方法指定定位点时就会出现图7。
drawString("String",x,y,width,height,Graphics. HCENTER | Graphics.BOTTOM); |
![]() |
Figure 7 |
♦ 3-2-3 图像的描绘
用Graphics类中的方法描绘画像。
• void drawImage(Image img, int x, int y, int anchor)
定位点的指定与Text的描绘一样。
♦ 3-3 事件和事件的处理
Canvas类实现了低级用户接口API、主要处理用户输入的按键事件。高级API要求实现listener接口中的事件手柄。用低级API,所有的低级事件都可以传送给Canvas类,因此,应用程序就必须重载一些事件处理的方法。以下是Canvas类的事件处理方法。
• showNotify()
• hideNotify()
• keyPressed()
• keyRepeated()
• keyReleased()
• paint()
这些方法被MIDP的线程按顺序调用。当一个事件处理的方法正在被执行时,其它的事件是不能被执行的。因此,这些方法中必须返回当前状态值。
♦ 3-3-1 Show事件和Hide事件
在画面显示Canvas之前,Canvas表示的就是Show事件。而且,Canvas在系统画面中被消除后,Hide事件就会产生。这些事件可调用以下的方法:
• void showNotify()
• void hideNotify()
这些方式,防止描绘卡通时的画面闪烁的对缓冲工作时,利用off screen缓冲(表示前在里面进行描绘的缓冲)可以解决画面闪烁问题。详细情况将在动画的讲解中详细说明。
♦ 3-3-2 按键事件
当用户按下键盘的键时,Canvas接受按键事件。每一个按键都有键代码。每一个键值和相应数值的ASII值相等。以下是每个按键和其编码。
按键 | 按键代码 | 按键代码的数值 |
0 | KEY_NUM0 | 48 |
1 | KEY_NUM1 | 49 |
2 | KEY_NUM2 | 50 |
3 | KEY_NUM3 | 51 |
4 | KEY_NUM4 | 52 |
5 | KEY_NUM5 | 53 |
6 | KEY_NUM6 | 54 |
7 | KEY_NUM7 | 55 |
8 | KEY_NUM8 | 56 |
9 | KEY_NUM9 | 57 |
* | KEY_STAR | 42 |
# | KEY_POUND | 35 |
另外,要从按键代码中获取按键名应使用以下Canvas的getKeyName()方法。
以下3种方式可以处理按键事件。
• void keyPressed(int keyCode)
• void keyReleased(int keyCode)
• void keyRepeated(int keyCode)
keyPressed方法是按键时的处理; keyReleased方法是释放键时的处理。KeyRepeated方法记述了短时间内同一键被按多次时的处理。
♦ 3-3-2-1 游戏的响应
一些应用程序,比如游戏,可能需要方向键的事件。Canva类中定义了一般游戏的响应。一个游戏响应可能关系到多个按键。以下所示的是N800的机型中游戏响应的常量和按键的对应值关系。
游戏动作 | 按键代码 | 按键代码的数值 |
UP | -1 | |
DOWN | -2 | |
LEFT | -3 | |
RIGHT | -4 | |
FIRE | -5 | |
GAME_A | KEY_NUM7 | 55 |
GAME_B | KEY_NUM9 | 57 |
GAME_C | KEY_STAR | 42 |
GAME_D | KEY_POUND | 35 |
由于游戏动作的按键代码的Mapping因机种而异,所以要想制作成能在N800以外的机种上也能正常工作的MIDP应用程序,就必须要提高移植性。为此,利用getGameAction()方式,将按键代码变换成游戏动作来使用。
♦ 3-4 重绘的显示
Screen的情况下,画面上的任意更改都会立刻自动反映到画面中。Canvas的情况下,即使在Canvas上加上更改到再描绘处理中也不会改变画面。以下是这个画面在描绘的方式。
• void repaint()
• void repaint(int x,int y,int width,int height)
调用上面的方法进行屏幕的更新。在repaint()中,画面整体都会被更新,而在repaint(int x,int y,int width,int height)中只有在指定范围内的画面才可能被更新。
制作应用程序
现在我们想利用低级API来制作应用程序。在上一讲中,我们讲述如何利用高级API来制作计算器的应用程序,在本讲中我们再讲述一下如何利用低级API来制作计算器的应用程序。
♦ 3-1 计算器应用程序的设计
本讲中所讲的计算器应用程序就是在按键上输入数字和运算符号。所以有必要获取按键事件。
如下是应用程序的类结构。
• N800Calculator2 (MIDlet main class)
• CalculatorCanvas (actual screen)
如下是画面结构:
• Title screen
• Calculator screen
主题画面中无论按住哪一个键,都会转至计算器的画面。
♦ 3-2 介绍状态变量
在本讲的应用程序中有主题画面和计算器画面。主题画面中无论按哪一个键,都会转至计算器的画面。在计算器画面中,用按键输入数字和运算符号进行计算,在屏幕中显示出结果。由于要将主题画面和计算器画面的不同的功能都在同一画面中管理,所以在本讲中的应用程序,使用表示现在画面的状态的状态变量。
状态变量在CalculatorCanvas类中有以下定义。
private final int STATE_TITLE = 0; //主题画面 private final int STATE_MAIN = 1; //计算器画面 private int state; //状态 |
Code Example 4
在状态变量state中用CalculatorCanvas类定义了STATE_TITLE,STATE_MAIN的2个常量。 由if语句判断应用程序的状态,如下所示:
if(state == STATE_TITLE){// 主题画面 记述处理 }else if(state==STATE_MAIN){// 计算器画面 记述处理 } |
Code Example 5
而且,即使是正在进行计算器画面表示时,其中仍会有状态。
计算器在开始的显示画面中显示为[0]。然后输入数字[3],屏幕中就会出现[3],再输入数字[4]屏幕上显示出[34]。再输入[5]后就成了[345]了。继续键入数字后,数字文字列就会突然终止,这些情况我们应该是知道的。但是,第一次按[+]后在输入数字[6],则在显示屏中并不显示成[3456],而只表示成[6]。
• 持续输入数字,到一定程度就会出现键入终止,所以必须先清除这些数字,之后再键入新的数字。
• 前者是数字的连续键入中,后者是运算符号的选择后,其他是所表示的数字是[0]的时候。
为能实现这样的处理,本讲中的应用程序就要使用名为clearFlag的boolean型变数。输入运算符号后直接将clearFlag变为true。输入数字时要检查clearFlag,true的话,清除表示中的数字表示所输入的数值。而false,则要终止表示数字的数字文字列。
//输入数字时的处理 if (keycode >= 48 && keycode <= 57) { if (clearFlag) { numString = ""; if (keycode == 48) { clearFlag = true; } else { clearFlag = false; } } numString += getKeyName(keycode); } |
Code Example 6
♦ 3-3 制作各个画面
依次制作主题画面、计算器画面。
描绘处理是关键,在CalculatorCanvas的paint方法中进行处理。
♦ 3-3-1 描绘画面背景
用如下的pait方法去实现背景颜色的设置:
// //设定背景 g.setColor(237, 237, 211); g.fillRect(0, 0, getWidth(), getHeight()); |
Code Example 7
♦ 3-3-2 制作主题画面
在主画面上显示图片和提示信息"Press any key",代码如下:(图8)
if(state == STATE_TITLE){ //表示主题画面 g.drawImage(titleImg, 0, 0, Graphics.LEFT | Graphics.TOP); //表示信息 g.setColor(0, 0, 0); g.drawString("Press Any Key!!",30,140,Graphics.LEFT | Graphics.TOP); } |
Code Example 8
![]() |
Figure 8 |
♦ 3-3-3 制作计算器画面
下面要讲的就是我们所需要的计算器画面了。能够显示出表示计算结果的显示屏和输入数字和运算符号的按键。
if(state==STATE_MAIN){//当是计算屏幕时 |
Code Example 9
在本讲中准备了表示按键 配置的画像,目的是让用户知道每个图片按钮的功能对应的相应按键。用DrawImage方式在空白处描绘出按键的画像。 另外,在数字的描绘、运算符号的描绘中使用变量(分别numString,operatorString)。按住按键时就会改变这些变量,也会反映在画面中。
![]() |
Figure 9 |
♦ 3-4 实现事件处理
♦ 3-4-1 命令事件
为了要结束应用程序和初始化计算器的数值,要使用命令事件。
if (command == clear) { // 当清除命令时 //初始化 tmp = 0; numString = "0"; operatorString = ""; clearFlag = true; repaint(); } if (command == exit) { // 当停止命令时 // 停止执行 try { ((N800Calculator2) midlet).destroyApp(true); } catch (Exception e) { } ((N800Calculator2) midlet).notifyDestroyed(); } |
Code Example 10
按EXIT命令后就会结束应用程序。调用N800Calculator2类(应用程序的主类)的destroyApp方法去实现这个功能。
♦ 3-4-2 按键事件
在主题画面中按键事件发生后,无论按哪个键都会转到计算器画面。在计算器画面中,按数字键就可以更新画面上的数字,按方向键上的运算符号后可以进行计算,并且再一次更新画面。
public void keyPressed(int keycode) { // 根据状态去选择 if (state == STATE_TITLE) {// 显示title // change the state state = STATE_MAIN; } else { // 主画面 switch (getGameAction(keycode)) { case Canvas.DOWN : //除 calculate(); operator = 3; operatorString = "/"; clearFlag = true; break; case Canvas.UP : // 乘 calculate(); operator = 2; operatorString = "*"; clearFlag = true; break; case Canvas.LEFT : // 加 calculate(); operator = 0; operatorString = "+"; clearFlag = true; break; case Canvas.RIGHT : // 减 calculate(); operator = 1; operatorString = "-"; clearFlag = true; break; case Canvas.FIRE : calculate(); operator = 4; operatorString = ""; clearFlag = true; break; default : break; } } //值得清除 if (keycode >= 48 && keycode <= 57) { if (clearFlag) { numString = "";//清空值 if (keycode == 48) { clearFlag = true; } else { clearFlag = false; } } numString += getKeyName(keycode); } repaint(); } |
Code Example 11
发生按键事件后会有画面显示的变化,因此要更新画面。在这些方法的最后调用repaint方法,进行画面的再描绘。
♦ 3-5 计算器的计算处理
用calculate方法处理计算逻辑。这个方法根据运算符号进行计算,运算结果的数字用numString字符串显示出来。
public void calculate() { try { int num = Integer.parseInt(numString); // Gets entered value // and performs calculation switch (operator) { case 0 : // 加法 tmp = tmp + num; break; case 1 : // 减法 tmp = tmp - num; break; case 2 : //乘法 tmp = tmp * num; break; case 3 : // 除法 tmp = tmp / num; break; default : // When "=" is selected tmp = num; //照原样输入所输入的数值 break; } } catch (Exception e) {//发生异常时 tmp = 0; e.printStackTrace(); } numString = String.valueOf(tmp); } |
Code Example 12
♦ 3-6 完成
如下是完成后的源码。 (N800Calculator2.zip)
在此范例程序中,为要清楚结构,只实际实现了有限的功能。以此为基础,可以进行制作真正计算器程序的挑战。
♦ 3-7 执行计算器程序
下面我们来执行已经制作完成的程序。
![]() | ![]() |
1.主题画面 | 2.计算器画面 |
![]() | ![]() |
3.输入[234] | 4.输入[+] |
![]() | ![]() |
5.输入[356]后再输入[=],显示出结果 | 6.选择命令清除键,清除数值 |
♦ 总结
在本讲的讲述中我们了解到利用低级API能够开发应用程序。由于使用的是低级API,所以能够在画面上进行各种各样的描绘,因此,我们制作应用程序的范围又加大了。我们在第2讲、第3讲中一并介绍了创建用户接口API的问题。在下一讲中,我们将主要介绍利用时间类制作含有动画的应用程序。
向N800中传送应用程序到本讲为止,制作完应用程序后在模拟器上进行执行确认。但是我们并没有在N800的实际机器上进行测试。因此,使用传送工具从PC向N800传送MIDlet应用程序。下面讲一下实际的操作方法。 顺序如下所示 1.编码2.制作jar文件 3.制作jad文件 4.使用传送工具传送 5. 真机操作 1. 编译 使用「NEC N800/N810/e530 Application Emulator」将制作完成的JAVA程序进行编译。 2. 制作jar文件下面我们来制作jar文件。在模拟器上进行操作确认也几乎没有什么变化。若不能顺利传送时,为了要在真机操作必须在参数「MIDlet-Description」的输入平台上的「MIDlet list」中重新记述「MIDlet-Description」。(图10) |
![]() |
Figure 10 |
3.制作jad文件 |
以下介绍怎样制作jad文件。参数的设定没必要特意更改模拟器上的TEST时。(图11) |
![]() |
Figure 11 |
4. 现在我们讲述使用传送工具进行传送的内容。 |
将N800和PC用数据线连接。随后就会出现「java Tool」。在「JAD file」平台上设定jad文件的路径,而在「JAR file」平台上设定jar文件。于是,调试PC与真机相连的部分,在「PortNum」平台上设定端口号。按「WriteFile」键,显示出传送完成则表示已经成功了。(图12) |
未传送成功的情况下,请尝试修改端口号。 |
![]() |
Figure 12 |
5.真机操作 |
传送完成之后就要进行真机操作了。插入电源,在“百宝箱”菜单中用上次传送的应用程序名(MIDlet-Name)设定名字。在上述情况下,加上N800Calculator2)的话,就会传送成功。一旦jar文件和jad文件不同,就会出现下载失败的信息。特别是,记述jad文件时千万不要忘记设定「MIDlet-Description」。一旦必须输入参数不足,则会出现下载错误。 |