我们从javax.microedition.lcdui.Canvas开始了解我们的低级UI,我们要用到低级UI必须要继承Canvas这个抽象类,在 Canvas的核心是paint()这个方法,这个方法做是负责绘制屏幕上的画面,每当屏幕需要重新绘制时,就会产生重绘事件时,系统就会自动调用paint(),并传入一个Graphics对象。
任何时候我们都可以通过调用reapaint()方法来产生重绘事件,它有两个方法,一个需要四个参数,分别用来指示起始坐标(X,Y),长宽,另一个则不需要任何参数,代表整个画面重新绘制。
我们可以通过getWidth()和getHeight() 方法获得Canvas的当前范围大小。每当Canvas 范围大小发生变化时,就会自动调用Canvas类的 sizeChanged()方法。
在低级UI里,我们可以直接把Graphics渲染到屏幕上,也可以在屏幕外合成到一个 Image中,已渲染的图形具体是合成Image还是显示到屏幕上,要看这个Graphics具体的来源而定,而渲染到屏幕上的Graphics对象将被送到paint()方法中来进行调度,这也是显示在屏幕上的唯一的途径,仅在paint()方法的执行期间这个应用程序可以对Graphics进行操作,至于要渲染到Image中的Graphics对象,当需要调用它的时候,可以通过Image.getGraphics()方法来取得相应的Graphics,它将可以被应用程序一直占有,在paint()方法运作的任何时候渲染到屏幕上,这也为我们在对不支持DoubleBuffered的手机开发提供了一些思路,可以通过Image来自行设计双缓冲区,避免图像出现所谓的撕裂现象。
与高级UI相比,低级UI就自由很多,任何时候我们可以调用repaint()产生重绘事件,调用完了repaint()会立刻返回,调用paint()回调函数则是由另一个专门的线程来完成。
底层事件大致可分为三类:Press Events(按键事件),Action Keys(动作按键,PointerEvents(触控事件)。
本节我们将围绕这三个主题来介绍一下这种事件的用法:
按键事件的几个核心方法为:keyPressed(),keyReleased(),keyRepeated(),当按键按下时会触发keyPressed(),当松开按键时,会触发keyReleased(),当长时间按住按键时会触发keyRepeated(),但是RepeatEvents不是JTWI要求强制支持的,所以使用之前要进行测试,看设备是否支持。在Canvas里面我们每按下一个按键都会触发keyPressed()函数,并传入相应位置的整数值,我们在MIDP规范中可以很容易的发现,KEY_NUM0——KEY——NUM9十个常数分别代表键盘上的0-9,还有两个功能键,KEY_STAR,KEY_POUND,如果我们传入的值小于0,代表我们传入了不合法的keycode,某些机器上还支持连续按键响应,但这并不是JTWI规定要支持的,所以我们在进行实际开发之前一定要用我们前面讲到的hasRepeatEvents()方法来进行判定。
动作按键主要针对游戏来设计的,在API中定义了一系列的动作事件:UP,DOWN,LEFT,RIGHT,GAME_A,GAME_B,GAME_C,GAME_D,当按下这些按键时会映射到我们自己为每个按键事件编写的方法,来完成一些动作。不过我们在MIDP2.0里我们已经有专门的游戏开发包了,所以我在这里就不重点介绍了。
触控事件主要面向高端设备,并非JTWI要求强制支持的,其核心方法为:pointerPressed(),pointerReleased(),pointerDragged(),分别对应我们通常所用的移动设备手写笔的点,击,拖拽几个动作,我们在这三个方法里可以定义相应的事件处理函数。在索爱P910C这样的高端手机上,支持屏幕的触控事件,我们在屏幕上点击,可以引发pointerPressed()函数,并传入当时位置的坐标,放开后,会引发pointerReleased()函数,同样也会传入坐标,具体的使用方法和keyPressed()以及keyReleased()大同小异,在后面的章节将会有对键盘及触控屏幕事件的详细叙述,同时大家可以参考一下WTK的说明文档获得比较详细的方法使用规则
我们在MIDP程序设计中用到的坐标系和一般我们平时用到的坐标系不一样,可见下图:
这是我们在绘制图象时要注意的。
下面我们来讲一讲Graphics这个对象,我们可以把它当作一个白纸,只要调用这个方法,我们就可以运用自己的想象力在这张白纸上画出自己想要的图案。
我们可以在WTK的控制台看到下面这个程序运行以后显示其Canvas的RGB值和灰度等参数,读者可以运行下面这个程序来获得对Graphics这个对象的初步了解。
下面我用一段简单的代码来说明一下这个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类提供的大量的绘图操作,这里给出了相关操作的方法列表供读者参考:
void
|
drawArc(intx, inty, intwidth, intheight, intstartAngle, intarcAngle)
Draws the outline of a circular or elliptical arc covering the specified rectangle, using the current color and stroke style.
|
void
|
drawChar(charcharacter, intx, inty, intanchor)
Draws the specified character using the current font and color.
|
void
|
drawChars(char[]data, intoffset, intlength, intx, inty, intanchor)
Draws the specified characters using the current font and color.
|
void
|
Draws the specified image by using the anchor point.
|
void
|
Draws a line between the coordinates (x1,y1) and (x2,y2) using the current color and stroke style.
|
void
|
drawRect(intx, inty, intwidth, intheight)
Draws the outline of the specified rectangle using the current color and stroke style.
|
void
|
drawRegion( Imagesrc, intx_src, inty_src, intwidth, intheight, inttransform, intx_dest, inty_dest, intanchor)
Copies a region of the specified source image to a location within the destination, possibly transforming (rotating and reflecting) the image data using the chosen transform function.
|
void
|
drawRGB(int[]rgbData, intoffset, intscanlength, intx, inty, intwidth, intheight, booleanprocessAlpha)
Renders a series of device-independent RGB+transparency values in a specified region.
|
void
|
drawRoundRect(intx, inty, intwidth, intheight, intarcWidth, intarcHeight)
Draws the outline of the specified rounded corner rectangle using the current color and stroke style.
|
void
|
Draws the specified String using the current font and color.
|
void
|
Draws the specified String using the current font and color.
|
void
|
fillArc(intx, inty, intwidth, intheight, intstartAngle, intarcAngle)
Fills a circular or elliptical arc covering the specified rectangle.
|
void
|
fillRect(intx, inty, intwidth, intheight)
Fills the specified rectangle with the current color.
|
void
|
fillRoundRect(intx, inty, intwidth, intheight, intarcWidth, intarcHeight)
Fills the specified rounded corner rectangle with the current color.
|
void
|
Fills the specified triangle will the current color.
|
主要是一组和绘画和填充的方法。请参考API的查看更多细节。
下面我们就来谈一谈如何用Graphics中线形的概念。如果我们需要绘制一条直线,我们可以调用drawLine()方法,需要定义其开始坐标和结束坐标,共四个参数,同时,Graphics提供两种形式的线条,一个是虚线,即Graphics.DOTTED,一个是实线,即Graphics.SOLID。
同样我们给出一段代码供大家参考:
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
public class test2 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); //虚线
g.setColor(125,125,125);
g.drawLine(10,10,100,100);
g.setStrokeStyle(Graphics.SOLID); //实线
}
}
用类似的方法,我们可以实现用Graphics的drawRect()和drawRoundRect()方法来绘制矩形和圆角矩形,我们也给出一段代码,让大家仔细观察一下两种矩形的区别:
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
public class test3 RectTestCanvas extends Canvas
{
public void paint(Graphics g)
{
clear(g) ;
g.setColor(255,0,0) ;
g.drawRect(5,5,100,20);
g.setColor(0,255,0) ;
g.fillRect(5,30,100,20);//fillRect()和drawRect()方法的区别在于一个填充
//一个不填充
g.setColor(0,0,255) ;
g.drawRoundRect(5,55,100,20,20,20);
g.setColor(255,0,255) ;
g.fillRoundRect(5,80,100,20,20,20);
}
public void clear(Graphics g)
{
//把屏幕清成白色
g.setColor(255,255,255);
g.fillRect(0,0,getWidth(),getHeight());
}
}
}
Canvas本身有两种状态,一种是普通默认情况下的,一种是全屏状态,可以用setFullScreenMode()方法来对其设定,两者之间的区别在于当我们使用全屏幕状态的时候,Title、Ticker以及我们的Command都无法在屏幕上显示。
当我们调用setFullScreenMode()的时候,不管是什么模式,都会调用seizeChanged()这个方法,并传入屏幕的高度和宽度作为其参数。
对于某些突发事件,比如说来电等等,屏幕会被系统画面所覆盖的时候,就会调用hideNotify()这个方法,当恢复原状时,就会调用我们原本的画面,那么系统就会同时调用showNotify()这个方法。在实际操作过程当中,应该覆写这两个方法,以便在可见性变化时,使程序做出相应的反应,Canvas会在它被显示的时候自动调用paint()方法,所以我们不必去调用repaint()方法。
下面给出一段代码,让大家体会一下如何在实际开发过程当中妥善处理屏幕事件:
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
public class test4 extends Canvas
implements CommandListener
{
public test4()
{
setTitle("全屏幕测试") ;
setTicker(new Ticker("Ticker ")) ;
addCommand(new Command("全屏幕",Command.SCREEN,1)) ;
addCommand(new Command("正常",Command.SCREEN,1)) ;
setCommandListener(this) ;
}
public void paint(Graphics g)
{
g.setColor(125,125,125);//灰色
g.fillRect(0,0,getWidth(),getHeight());
g.setColor(0,0,0);//黑色
g.drawLine(10,10,150,10);
}
public void commandAction(Command c,Displayable s)
{
String cmd = c.getLabel();
if(cmd.equals("全屏幕"))
{
setFullScreenMode(true) ;
}else if(cmd.equals("正常"))
{
setFullScreenMode(false) ;
}
}
protected void sizeChanged(int w,int h)
{
System.out.println("改变后的宽度:"+w) ;
System.out.println("改变后的高度:"+h) ;
}
protected void hideNotify()
{
System.out.println("屏幕被系统遮蔽") ;//会在WTK控制台中显示,
//读者需要注意
}
protected void showNotify()
{
System.out.println("屏幕显示在屏幕上") ;
}
}
从截图1和截图3可以看出全屏幕和普通模式的区别,全屏幕的Canvas的显示区域覆盖了原来显示标题和Ticker的地方。
如果我们需要在Canvas里处理我们的按键事件,我们必须覆写 Canvas的keyPressed(),keyReleased()和keyReapeated()这三个方法,其中keyReapeated()方法JTWI并未做硬性规定,所以我们在开发的时候一定要用Canvas.hasRepeatedEvents()方法来进行实际的侦测,当按下按键时会触发keyPressed()方法,松开时会引发keyReleased()方法 ,长时间按住的话则会引发keyRepeated()方法,JTWI硬性规定MIDP2.0的目标设备必须硬性支持ITU-T的电话键盘,即必须使数字0到9,“*”,“#”必须能在 Canvas中得到定义,当然我们也可以扩充其他按键,但是这样对程序的移植就会有影响,下面我们通过代码来看看如何在实际开发中运用上面提到的三个方法。
按下前:
按下后:
代码:
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
public class test5 extends Canvas
implements CommandListener
{
public test5()
{
addCommand(new Command("测试",Command.SCREEN,1)) ;
setCommandListener(this);
}
boolean pressed = false ;
public void paint(Graphics g)
{
g.setColor(125,125,125);
g.fillRect(0,0,getWidth(),getHeight());
if(pressed)
{
g.setColor(0,0,0);
g.drawLine(20,20,120,20);
g.drawLine(20,20,20,100);
g.setColor(255,255,255);
g.drawLine(120,20,120,100);
g.drawLine(20,100,120,100);
}else
{
g.setColor(255,255,255);
g.drawLine(20,20,120,20);
g.drawLine(20,20,20,100);
g.setColor(0,0,0);
g.drawLine(120,20,120,100);
g.drawLine(20,100,120,100);
}
}
public void commandAction(Command c,Displayable s)
{
System.out.println("Command Action");
}
protected void keyPressed(int keycode)
{
System.out.println("Key Pressed");
pressed = true ;
repaint() ;
}
protected void keyReleased(int keycode)
{
System.out.println("Key Released");
pressed = false ;
repaint() ;
}
}
对于触控事件(pointer events)的方法,应用程序可以通过覆写pointerPressed(),pointerReleased()和pointerDragged方法,(分别对应于手写笔的按下,松开,拖拽三个动作)其处理过程和按键处理几乎一致,所以这里不螯述。
在这一节里面我们通过设计一个稍微复杂一点的动画来体现Graphics在实际开发当中带来的便利,在这里给出几段曾经对我们启发很大的代码,通过围绕这几段代码进行分析,来掌握Graphics在实际开发当中的作用。
前面我们谈到了双缓冲区问题,我们先就Image这个类来谈一谈。
在介绍Image之前,先介绍几个比较基础的概念,无论是图像还是文字在Graphics中都是通过锚点(anchor points)来控制它们具体的方位,对于Image而言有如下几个锚点常量:LEFT,RIGHT,HECENTER,TOP,VCENTER,BOTTOM, BASELINE。其具体位置对应如下:
Image分为可变和不可变两种类型的,不可变的Image是从资源文件,二进制数据,RGB数值,及其他Image直接创建的,一旦创建完成,Image就无法再变化。不可变的Image通过Image.createImage(String name) 方法从指定的路径中读取需要创建Image所必须的数据,注意参数中的字符串必须以“/”打头,并且包括完整的名称。
可变的Image以给定的大小创建,它是可以修改的,可变的Image由Image.createImage(int width,int height)方法来创建,需要给定长宽,Image的其他显示特性和机器的显示屏完全一致。
我们前面提到了撕裂现象,它产生的原因是因为显示屏在显示图像前都会先参照影象内存,然后当绘制速度慢到一定程度时,显示在屏幕上的画面会由前一帧画面的一部分和后一帧画面的一部分组成,这样造成图像“撕裂”,为了解决这个问题,手机厂商可以从硬件上支持DoubleBuffer,即双缓冲区,这样显示屏绘图的时候都可以使用一个影象内存来绘图,另一个影象内存来进行程序绘图,这样交叉进行,可以避免画面撕裂的产生。
如果硬件厂商并未在硬件上支持DoubleBuffer怎么办呢?我们可以从程序上着手,自己设计一个双缓冲区。
代码片段如下:
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
public class test6 extends Canvas
implements Runnable,CommandListener
{
Command start = new Command("开始",Command.OK,1) ;
Command stop = new Command("停止",Command.STOP,1) ;
private Image offscreen ;
public test6)
{
addCommand(start);
setCommandListener(this) ;
if(isDoubleBuffered())
{
System.out.println("支持双缓冲区");
}else
{
System.out.println("不支持双缓冲区,启动自制双缓冲区");
offscreen = Image.createImage(getWidth(),getHeight());
}
}
public void paint(Graphics g)
{
if(isDoubleBuffered())
{
System.out.println("On-Screen绘图");
clear(g);
paintAnimation(g,100,10,r) ;
paintCross(g,x,y,length) ;
}else
{
System.out.println("Off-Screen绘图");
Graphics offg = offscreen.getGraphics() ;
clear(offg) ;
paintAnimation(offg,100,10,r) ;
paintCross(offg,x,y,length) ;
g.drawImage(offscreen,0,0,0);
}
}
public void clear(Graphics g)
{
//把屏幕清成白色
g.setColor(255,255,255);
g.fillRect(0,0,getWidth(),getHeight());
}
int r = 0 ;
public void paintAnimation(Graphics g,int x,int y,int l)
{
g.setColor(0,0,0);
g.drawRect(x,y,l,l);
}
int x =50 ;
int y =50 ;
int length = 5 ;
public void paintCross(Graphics g,int x,int y,int length)
{
g.setColor(255,0,0);
g.drawLine(x-length,y,x+length,y);
g.drawLine(x,y-length,x,y+length);
}
boolean conti = false ;
public void commandAction(Command c,Displayable s)
{
String cmd = c.getLabel() ;
if(cmd.equals("停止"))
{
conti = false ;
removeCommand(stop);
addCommand(start) ;
}else if(cmd.equals("开始"))
{
removeCommand(start);
addCommand(stop) ;
conti = true ;
Thread t = new Thread(this);
t.start();
}
}
int rate = 50 ; //每1/20秒画一次
public void run()
{
long s = 0 ;
long e = 0 ;
long diff = 0 ;
while(conti)
{
s = System.currentTimeMillis() ;
r++;
if (r > 10)
r = 0;
repaint();
serviceRepaints() ;
e = System.currentTimeMillis() ;
diff = e-s ;
if(diff<rate)
{
try
{
Thread.sleep(rate-diff);
}catch(Exception exc){}
}
}
}
protected void keyPressed(int keycode)
{
switch(getGameAction(keycode))
{
case Canvas.UP :
y = y-2 ;
break ;
case Canvas.DOWN :
y = y+2 ;
break ;
case Canvas.LEFT :
x = x-2 ;
break ;
case Canvas.RIGHT :
x = x+2 ;
break ;
}
repaint();
}
}
需要提醒各位读者注意的是Image.createImage()非常浪费内存,我们最好能够尽量重复使用它。
Graphics中还提供了对了对字体的控制方法,每个Graphics都有一个Font对象与其关联,来进行文字的渲染操作,调用其类方法setFont(null),即可使字体恢复到默认状态,对于具体的参数,Font提供了以下常量,来控制Font的属性:
字体大小:SMALL、MEDIUM、LARGE
字体外观:PROPORTIONAL、MONOSPACE、SYSTEM
字体风格:PLAIN、BOLD、ITALIC、UNDERLINED
通过charWidht(),charsWidth(),stringWidth(),substringWidth()来获得字符串,字符,字符集合的宽度,垂直方面则可以参考getHeight()和getBaselinePosition()方法获得。
当你不对Font进行设定时,机器会自动从设备中选择最合适的 Font属性。
分享到:
相关推荐
第四章“MIDP 低级UI 的使用” 介绍了MIDP 的不可移植UI API,我们称之为低级UI。 利用他你可以更加自由的绘画你的UI。你将了解到关于事件处理的很多知识。 第五章“MIDP 的持久化解决方案— RMS” 为我们讲解了数据...
第四章“MIDP低级UI的使用” 介绍了MIDP的不可移植UI API,我们称之为低级UI。利用他你可以更加自由的绘画你的UI。你将了解到关于事件处理的很多知识。 第五章“MIDP的持久化解决方案 — RMS” 为我们讲解了数据...
第四章“MIDP 低级UI 的使用” 介绍了MIDP 的不可移植UI API,我们称之为低级UI。 利用他你可以更加自由的绘画你的UI。你将了解到关于事件处理的很多知识。 第五章“MIDP 的持久化解决方案 — RMS” 为我们讲解了...
我们将继承自Canvas,因为我们要进行低级UI绘制。Canvas类提供了一个画布,我们可以直接在其上绘制图形。我们需要重写paint()方法来实现绘图逻辑。 ```java public class MyCanvas extends Canvas { public void ...
第四章“MIDP低级UI的使用” 介绍了MIDP的不可移植UI API,我们称之为低级UI。利用他你可以更加自由的绘画你的UI。你将了解到关于事件处理的很多知识。 第五章“MIDP的持久化解决方案 — RMS” 为我们讲解了数据...
由于资源有限,J2ME提供了两种主要的UI框架:高级UI(MIDP CLDC)和低级UI(通常基于Graphics类)。本教程将深入探讨如何使用低级UI来构建菜单和简单的通讯录应用。 首先,我们要理解低级UI的基本概念。低级UI是...
#### 四、MIDP低级UI的使用 - **低级UI**:不可移植的UI API,允许开发者更灵活地控制UI的绘制过程。 - **事件处理**:深入探讨事件监听器、事件源等机制,以及如何处理触摸屏、键盘等输入设备的事件。 #### 五、...
在J2ME中处理触摸屏事件主要依赖于MIDP(Mobile Information Device Profile)的低级图形API,如`Graphics`类和`Pointer`接口。开发者需要监听`PointerEvent`,通过`PointerEvent.getX()`和`PointerEvent.getY()`...
目 录 第一章 绪论 1 1.1 研究背景 1 1.2 研究内容 1 第二章 J2ME及其体系结构概述 2 ...2.5.3 低级UI 15 第三章 手机游戏开发过程 16 3.1 贪吃蛇游戏的规则简介以及开发环境 16 3.1.1 贪吃蛇游戏的规则简介 16 3.
而低级UI(Non-Portlet UI)则提供了更底层的控制,适合开发更复杂或定制化的界面。 此外,教程还会涵盖如何搭建J2ME开发环境,包括选择合适的集成开发环境(IDE)、配置模拟器或真机测试,以及部署应用到实际设备...
例如,使用低级游戏编程接口(LWJGL)或MIDP(Mobile Information Device Profile)中的游戏API进行游戏开发。这包括对游戏循环、用户输入、动画、音效和碰撞检测等概念的理解。 ### 移动新技术栏目 随着技术的...
第一章 绪论 1 1.1 研究背景 1 1.2 研究内容 1 第二章 J2ME及其体系结构概述 2 ...2.5.3 低级UI 15 第三章 手机游戏开发过程 16 3.1 贪吃蛇游戏的规则简介以及开发环境 16 3.1.1 贪吃蛇游戏的规则简介 16 3.1.2 开
**J2ME游戏开发教程详解** Java 2 Micro Edition(J2ME)是Java平台的一个子集,专门设计用于资源有限的设备,如移动电话、智能手表和家用电器等。在2000年代初,J2ME是移动游戏开发的重要平台,许多经典手机游戏都...
第一章 绪论 1 1.1 研究背景 1 1.2 研究内容 1 第二章 J2ME及其体系结构概述 2 ...2.5.3 低级UI 15 第三章 手机游戏开发过程 16 3.1 贪吃蛇游戏的规则简介以及开发环境 16 3.1.1 贪吃蛇游戏的规则简介 16 3.1.2 开
**J2ME(Java 2 Micro Edition)实用教程——手机游戏开发** J2ME是Java平台的一个重要组成部分,主要用于嵌入式系统,如移动设备、智能家电等。它提供了丰富的功能,让开发者能够构建功能强大的应用程序,特别是...
本新手教程将带你从零基础开始,逐步掌握J2ME的核心概念和技术。 一、J2ME概述 J2ME由Java ME规范定义,包括配置(Configurations)和框架(Profiles)。配置定义了运行时环境的基本硬件需求,如CLDC(Connected ...
本篇将深入探讨 J2ME UI 的低级界面开发实例,包括图片加载和按钮选择等功能。 首先,我们要理解 J2ME UI 开发的基础组件,主要由 MIDP (Mobile Information Device Profile) 提供。MIDP 包含了 CLDC (Connected ...
2. **字符编码**:J2ME平台通常使用Unicode编码,所以你需要熟悉Unicode编码表,尤其是中文字符的部分。每个中文字符对应一个或多个Unicode码点。 3. **输入法引擎**:为了将按键序列转换为中文字符,你需要实现一...