骑猪闯天下

J2ME随笔,记录成长的脚步

统计

留言簿(3)

阅读排行榜

评论排行榜

[J2ME] MIDP低级界面开发

MIDP低级界面开发
<转载>

MIDP低级界面开发——使用LCDUI低级API
高级API使用简单、有很高的可移植性,却无法控制许多细节。要对界面更多的进行控制,必须使用低级API。
5.1 Canvas类开发简介
低级界面屏幕都继承自名为Canvas的屏幕类。Canvas类提供了一系列键盘低级事件和绘图接口,具体的绘图操作则由一个名为Graphics的图形类来完成。
5.1.1 Canvas类简介
Canvas即画布,可以在其上绘制不同图案。Canvas提供了一个绘图接口方法paint(Graphics g),凡是继承Canvas的继承类都必须实现paint()方法,因此可以在paint方法中实现屏幕的绘画代码。
示例:
class MyCanvas extends Canvas
{
public void paint(Graphics g)
{

}
}
paint方法传入了Grpahics类型的参数g,具体的绘画将由Graphics的g实现。可以看出Canvas类和Graphics类的关系是画布与画笔的关系。
5.1.2 低级API与低级事件
Canvas可以处理低级事件,但并非处理所有的系统事件。设备支持哪些系统事件,必须由硬件的支持程度来判断。Canvas提供一些方法判断硬件支持程度。
功能
检测方法
低级事件/回调函数
键盘事件
支持
keyPressed(int keycode)
keyReleased(int keycode)
屏幕事件
支持
showNotity()
hideNotify()
重绘事件
支持
paint(Graphics g)
是否支持双缓冲
Canvas.isDoubleBuffered()

是否支持repeat
Canvas.hasRepeatdEvent()
keyRepeated(int keycode)
是否支持触控屏幕事件
Canvas.hasPointerEvents()
pointerPressed(int x, int y)
pointerReleased(int x, int y)
是否支持触控屏幕拖拽事件
Canvas.hasPointerMotionEvnets()
pointerDragged(int x, int y)
机器一定会支持的键盘事件有keyPressed()、keyReleased(),屏幕事件showNotify()、hideNotidy(),以及重绘事件paint()。
注意:除了showNotidy()之外,其它回调函数只有在此Canvas是目前屏幕上的画面时才会被调用。
5.1.3 重绘事件
示例:
//CanvasTestMidlet.java
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class CanvasTestMidlet extends MIDlet
{
private Display display;
public CanvasTestMidlet()
{
display = Display.getDisplay(this);
}
public void startApp()
{
MyCanvas mc = new MyCanvas();
display.setCurrent(mc);
}
public void pauseApp()
{
}
public void destroyApp(boolean unconditional)
{
}
}
//MyCanvas.java
import javax.microedition.lcdui.*;
public class MyCanvas extends Canvas
{
/** Creates a new instance of MyCanvas */
public MyCanvas()
{
}
public void paint(Graphics g)
{
}
}
任何时候都可以自行调用repaint()产生重绘事件。repaint()有两个同名方法,其中一个需要四个参数,用来指定重画区域的X、Y坐标,宽度与高度;另外一个无参数,代表重绘整个屏幕。调用repaint()之后会立刻返回,继续下面工作,调用paint()回调函数的工作则由一个专门处理UI的线程来完成。若希望等到paint()完成后再返回,可以在repaint()之后立刻调用serviceRepaints()方法。
注意:serviceRepaints()用来强制队列中的重绘事件快点做完,如果队列中没有重绘事件,则serviceRepaints()什么也不会做,因此在调用serviceRepaints()之前,通常伴随一个repaint()。
5.1.4 坐标系统
在使用绘图函式前,请先注意MIDP 中X 坐标与Y 坐标的定义方式。传统的笛卡尔坐标其原点在左下角,向右X 坐标值递增,向上Y坐标值递增。
但是我们在手机的屏幕上做图时,变成向右X 坐标值递增,向下Y 坐标值递增。
5.1.5 像素(Pixel)
我们在所有图形相关函数之中所使用的坐标所代表的并非像素本身,而是指像素和像素之间的空格所构成的坐标,如下图所示:
像素与像素之间所构成的坐标
所以一般我们所说的坐标(3,1)并非指位于(3,1)这个像素,而是指像素(2,0)、(2,1)、(3,0)、(3,1)所包围的这个部分。也正因为坐标指的并非图素本身,所以会造成在绘制图型和填满区块时有所差异,这两者的不同我们将在以后说明。
5.1.6 Graphics入门
paint(Graphics g)方法会传入一个Graphics对象作为参数,可以把该对象当作是一个抽象的画笔,调用Graphics的方法,就可以在这个画布上绘图。
编写我们自己的Canvas时,要做的第一件事就是把画面清空,然后才开始绘图,避免画面上残留前一个画面所遗留下的东西。
//清屏
public void paint(Graphics g)
{
g.setColor(255, 0, 255);
g.fillRect(0, 0, getWidth(), getHeight());
… …
}
上述范例中,我们使用Graphics的setColor()来设置画笔颜色:
setColor(int r, int g, int b)注意,rgb的值限定在0~255之间。
或setColor(int rgb) 直接传入0x00RRGGBB这样的整数。
设定好颜色后,可以使用getRedComponent()、getGreenComponent()、getBlueComponent()分别取得R、G、B的颜色设定。或者直接使用getColor()取得0x00RRGGBB这样的整数,也就是说,最后第0~7 位代表蓝色、8~15 代表蓝色,16~23 代表红色。
getDisplayColor()较特殊,返回机器上绘图时真正使用的颜色。有些机器不具有显示所有颜色的能力。屏幕的灰度数可以用getGrayScale()取得,也可以用setGrayScale(),灰度数取值在0~255之间。使用这个函式的时候请特别注意,如果您已经使用了相对应的setGrayScale()来设定灰阶色阶数,那么getGrayScale()函式只是单纯地传回设定值。但是如果您没有设定灰阶色阶数,那么呼叫getGrayScale()函式的时候,会导致系统利用目前作用色的R、G、B 值来做大量运算,并求得最接近的灰阶色阶数。
5.1.7 绘制直线
我们可以使用Graphics 类别的drawLine()函式绘制线段。DrawLine 的四个参数分别是起点X 坐标,起点Y 坐标、终点X 坐标、终点Y 坐标。举例来说,如果我们函式呼叫为:g.drawLine(1,1,1,6)
则实际绘制的线段如下图所示:
实际绘制出来的线段的位置
我们可以发现坐标右边的相素都会被填满。
如果我们函式调用为:
g.drawLine(1,1,6,1)
则实际绘制的线段如下图所示:
实际绘制出来的线段的位置
我们可以发现坐标下方的像素都会被填满。
当我们绘图形时,有所谓的笔触(stroke style)。Graphics提供两种笔触,分别是Graphics.SOLID和Graphics.DOTTED:
g.setStrokeStyle(Graphics.DOTTED);
相应的取得目前所用笔触:g.getStrockStyle()
5.1.8 画弧形
我们可以使用Graphics 类的drawArc()方法绘制弧形。drawArc 共有6 个参数,它们分别是:前四个决定弧形所在的矩形范围,第五个决定起始角度,第六个参数则决定弧形本身所涵盖的角度。
如果我们方法调用为:
g.drawArc(20,10,width,height,45,90);
则实际绘制的弧形如下图所示:
实际绘制出来的弧形
填充弧形
我们可以使用Graphics 类别的fillArc()函式填充弧形。fillArc 共有6 个参数,它们分别是:前四个决定弧形所在的矩形范围,第五个决定起始角度,第六个参数则决定弧形本身所涵盖的角度。
如果我们方法调用为:
g.fillArc(20,15,width,height,45,90);
则实际绘制的填充弧形如下图所示:
实际绘制出来的填充弧形
5.1.9 矩形
画矩形:我们可以使用Graphics 类别的drawRect()函式绘制矩形。drawRect 有4 个参数,分别是起点X 坐标、起点Y 坐标、宽度、长度。
如果我们函数调用为:
g.drawRect(1,1,6,8)
则实际绘制的矩形如下图所示:
实际绘制出来的矩形
我们可以发现所构成的矩形路径,其右边和下方的像素都被填满了。
画圆角矩形:我们可以使用Graphics 类别的drawRoundRect()函式绘制圆角矩形。其实drawRoundRect()和drawRect()函式的前四个参数意义相同,唯一的差距只有在最后两个参数,它们分别是圆角所在矩形的宽度,以及圆角所在矩形的高度。
如果我们方法调用为:
g.drawRoundRect(1,1,6,8,arcWidth,arcHeight)
则实际绘制的圆角矩形,在矩形的部分和使用drawRect()的结果相同,差别只有在四个直角的样子不再是直角,而变成圆角。
如下图所示:
实际绘制出来的圆角矩形
填充矩形:我们可以使用Graphics 类别的fillRect()函式填充矩形。fillRect 有4 个参数,分别是起点X 坐标、起点Y 坐标、宽度、长度。
如果我们方法调用为:
g.fillRect(1,1,6,8)
则实际绘制的矩形如下图所示:
实际绘制出来的填充矩形
我们可以发现只有包含在矩形路经之内的图素才会被填满,这和drawRect()函式的结果有所不同(上下都差一个图素的大小)。
填充圆角矩形:我们可以使用Graphics 类别的fillRoundRect()函式填充圆角矩形。其实fillRoundRect()和fillRect()函式的前四个参数意义相同,唯一的差距只有在最后两个参数,它们分别是圆角所在举行的宽度,以及圆角所在矩形的高度。
如果我们方法调用为:
g.fillRoundRect(1,1,6,8,arcWidth,arcHeight)
则实际绘制的圆角矩形,在矩形的部分和使用fillRect()的结果相同,差别只有在四个角的样子不再是直角,而变成圆角,如下图所示:
实际绘制出来的填充圆角矩形
5.1.10 三角形
绘制三角形,使用三个顶点,分别画线即可,因此MIDP只提供填充三角形的功能:
g.fillTriangle(int x1, int y1, int x2, int y2, int x3, int y3) ;
练习:尝试自己绘制三角形,并填充它
5.2 Canvas与屏幕事件处理
Canvas本身具有两种状态:
􀁺 普通状态
􀁺 全屏状态
可以使用setFullScreenMode()设定Canvas状态。
示例:
/*
* FullScreenCanvas.java
*
* Created on 2006年2月26日, 上午5:54
*/
import javax.microedition.lcdui.*;
/**
*
* @author Allan
*/
public class FullScreenCanvas extends Canvas implements CommandListener
{
public FullScreenCanvas()
{
setTitle("Full screen test");
setTicker(new Ticker("running..."));
addCommand(new Command("full screen", Command.SCREEN, 1));
addCommand(new Command("normal", Command.SCREEN, 1));
setCommandListener(this);
}
public void paint(Graphics g)
{
g.setColor(255, 255, 255);
g.fillRect(0, 0, getWidth(), getHeight());
}
public void commandAction(Command c, Displayable s)
{
String cmd = c.getLabel();
if (cmd.equals("full screen"))
{
setFullScreenMode(true);
}
else if (cmd.equals("normal"))
{
setFullScreenMode(false);
}
}
public void sizeChanged(int w, int h)
{
System.out.println("width:" + w);
System.out.println("height:" + h);
}
public void hideNotify()
{
System.out.println("应用程序区域被覆盖");
}
public void showNotify()
{
System.out.println("屏幕显示");
}
}
几个重要观念:
1) 对于Canvas低级API,标题、Ticker、Command区域依然有用;
2) 全屏模式下,标题、Ticker、Command区域无法显示,但原本对应到按钮的地方仍然存在,只是看不见;
3) 调用setFullScreenMode()时,不管设置成全屏模式还是正常模式,sizeChanged()都会被调用,并传入当前屏幕的高度和宽度。
4) 当屏幕被系统画面(如菜单、来电显示等)覆盖时,会自动调用hideNotify(),告知应用程序目前的画面被覆盖了。当这些系统画面消失时,系统将调用showNotify()告知应用程序。Canvas第一次显示在屏幕上时,系统也会调用showNotify()。Canvas移出屏幕(有其它displayable被显示,调用setCurrent())时,hideNotify()会被调用。
5) 当我们使用Graphics绘图时,零坐标会随着模式的改变而改变,所以零点不是绝对的,而是相对于应用程序区而言。因为屏幕的大小会改变,所以在清除屏幕(fillRect())的时候使用canvas.getHeight()和canvas.getWidth()取得屏幕大小,而不是固定值。
5.3 键盘事件处理
当Canvas子类正作用于屏幕时,按下任何按钮,就会引发keyPressed()方法,并传入一个代表该按钮的整数,而放开按钮之后,会引发keyReleased()方法,并传入一个代表该按钮的整数值。系统如果传入小于0的值,则为不合法keycode。
某些机器上还可以支持连发事件(即一直按着按钮持续一段时间),该事件会引发keyRepeated()
方法,并传入一个代表该按钮的数值,但该事件并非所有机器都支持,所以我们有必要使用Canvas类中的hasRepeatEvents()方法询问系统是否支持连发事件。
示例:
import javax.microedition.lcdui.*;
public class KeyEventTestCanvas extends Canvas
{
private boolean pressed = false;
public KeyEventTestCanvas()
{
}
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);
}
}
protected void keyPressed(int keycode)
{
pressed = true;
repaint();
}
protected void keyReleased(int keycode)
{
pressed = false;
repaint();
}
}
此例中,把
protected void keyPressed(int keycode)
{
pressed = true;
repaint();
}
protected void keyReleased(int keycode)
{
pressed = false;
repaint();
}
改为
protected void keyPressed(int keycode)
{
repaint();
pressed = true;
}
protected void keyReleased(int keycode)
{
repaint();
pressed = false;
}
结果是一样的,这是因为回调函数都是在同一个UI的线程中执行,因此理论上,repaint()只是产生一个重绘事件就立刻返回,系统必须等到keyPressed()/keyReleased()执行完毕之后才能继续调用paint()重绘屏幕。
5.4 键盘响应
Canvas里定义了几个常数
KEY_NUM0、KEY_NUM1 ~ KEYNUM9
KEY_STAR、KEY_POUND共12个
可以利用这几个常数判定事件处理方法所传进来的keyCode,得知那个按钮被按下。
为了程序可以跨平台执行,建议使用这些标准的定义键。
玩游戏的时候通常2、4、6、8分别代表上、下、左、右,星字键代表发射,井字代表跳跃。但并非所有机器上都会有相同的键盘排列方式,也并非一定按照此方式设定。为了设计Game的时候方便,MIDP规范中,Canvas中定义了几个与Game键盘代码相关的常数,分别是UP、DOWN、LEFT、RIGHT、FIRE、GAME_A、GAME_B、GAME_C、GAME_D。这些定义与之前的定义有所重复,但是因为有了一些抽象性,在移植的时候也就方便多了。
常用方法:
1. getGameAction(int keyCode);
该方法传入keyCode,会返回所代表的Game键盘代码。
switch(getGameAction(keyCode))
{
case Canvas.FIRE:
fire();
break;

}
2. getKeyCode()
该方法传入Game键盘码,返回所代表的keyCode。
if (keyCode == getKeyCode(Canvas.LEFT))
{
moveLeft();
}
3. 可以利用Canvas的getKeyName()取得该keyCode所代表的按键名称
示例:
import javax.microedition.lcdui.*;
/**
*
* @author Allan
*/
public class KeyEventTestCanvas extends Canvas
{
private boolean pressed = false;
//cross坐标
private int x;
private int y;
private final int length = 20;
private int dxy = 5;
/** Creates a new instance of KeyEventTestCanvas */
public KeyEventTestCanvas()
{
x = getWidth()/2;
y = getHeight()/2;
if (this.hasRepeatEvents())
{
System.out.println("支持连发");
}
else
{
System.out.println("不支持连发");
}
}
public void paint(Graphics g)
{
g.setColor(128, 128, 128);
g.fillRect(0, 0, getWidth(), getHeight());
paintButton(g, 10, 10, 120, 90, pressed);
paintCross(g, x, y, length);
}
public void paintButton(Graphics g, int x, int y, int w, int h, boolean pressed)
{
if (pressed)
{
g.setColor(0, 0, 0);
g.drawLine(x, y, x+w, y);
g.drawLine(x, y, x, y+h);
g.setColor(255, 255, 255);
g.drawLine(x+w, y, x+w, y+h);
g.drawLine(x, y+h, x+w, y+h);
}
else
{
g.setColor(255, 255, 255);
g.drawLine(x, y, x+w, y);
g.drawLine(x, y, x, y+h);
g.setColor(0, 0, 0);
g.drawLine(x+w, y, x+w, y+h);
g.drawLine(x, y+h, x+w, y+h);
}
}
/*
*@parameter x 十字中心点x坐标
*@parameter y 十字中心点y坐标
*/
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);
}
public void keyPressed(int keycode)
{
//打印keycode代表的按键名称
//System.out.println(getKeyName(keycode));
int action = getGameAction(keycode);
switch (action)
{
case Canvas.UP:
y -= dxy;
break;
case Canvas.DOWN:
y += dxy;
break;
case Canvas.LEFT:
x -= dxy;
break;
case Canvas.RIGHT:
x += dxy;
break;
case Canvas.FIRE:
pressed = true;
break;
}
repaint();
}
public void keyReleased(int keycode)
{
int action = getGameAction(keycode);
switch (action)
{
case Canvas.FIRE:
pressed = false;
break;
}
repaint();
}
public void keyRepeated(int keycode)
{
int action = getGameAction(keycode);
switch (action)
{
case Canvas.UP:
y -= dxy;
break;
case Canvas.DOWN:
y += dxy;
break;
case Canvas.LEFT:
x -= dxy;
break;
case Canvas.RIGHT:
x += dxy;
break;
case Canvas.FIRE:
pressed = true;
break;
}
repaint();
}
}
5.5 触控屏幕的事件处理
当Canvas是当前画面,数控笔在屏幕上点击,就会引发pointerPressed()方法,并传入其
当前处于屏幕的x与y坐标,放开出控笔后,会引发pointerReleased()方法,并传入当前屏幕位置的x与y坐标。某些机器上可以产生拖拽事件(即一直按住屏幕拖拽),该事件会引发pointerDragged()方法,并传入当时处于屏幕位置的x与y坐标。
并非所有设备都支持触控事件,我们可以使用Canva类中的hasPointerEvents()方法获得系统是否支持触控笔事件的信息。
并非所有设备都支持拖拽事件, 我们可以使用Canvas类中的hasPointerMotionEvents()方法判断系统是否支持触控笔拖拽事件。
示例:
public class PointerEventTestCanvas extends Canvas implements CommandListener
{
private int x1;
private int y1;
private int x2;
private int y2;
private Command backCommand;
/** Creates a new instance of PointerEventTestCanvas */
public PointerEventTestCanvas()
{
backCommand = new Command("返回", Command.BACK, 1);
addCommand(backCommand);
setCommandListener(this);
if (hasPointerEvents())
{
System.out.println("支持数控笔");
}
else
{
System.out.println("不支持数控笔");
}
if (hasPointerMotionEvents())
{
System.out.println("支持数控笔拖拽");
}
else
{
System.out.println("不支持数控笔事件");
}
}
public void paint(Graphics g)
{
g.setColor(255, 255, 0);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(0, 0, 0);
g.drawLine(x1, y1, x2, y2);
}
public void pointerPressed(int x, int y)
{
x1 = x;
y1 = y;
}
public void pointerReleased(int x, int y)
{
x2 = x;
y2 = y;
repaint();
}
public void pointerDragged(int x, int y)
{
x2 = x;
y2 = y;
repaint();
}
public void commandAction(Command c, Displayable s)
{
if (c == backCommand)
{
}
}
}
5.6低级事件和高级事件同时出现
当高级事件和低级事件同时出现时,系统会自动判断。如果按钮属于系统的,就会交给高级
事件处理方法来处理,如果不是,才会由低级事件来做。
5.7 绘制字符串
Graphics类提供了绘制字符串的方法,原形如下:
public void drawString(String str, int x, int y, int anchor)
参数:
x、y:相对于屏幕原点的x、y坐标
anchor:定位点
注意:即使x、y相同,anchor不同,具体位置还是不同的(后面详述)
另外,需要了解的是字符串本身显示要占用屏幕上的一个矩形的空间。anchor的作用就是设置字符串所处矩形区域位于屏幕坐标的哪个位置。
5.8 Image类
Image 类是我们在处理图形时常常会用的类别,如果根据它的产生方式, 我们可以细分成可修改( mutable ) 和不可修改(immutable)两种。要辨别一个Image 对象是可修改还是不可修改,您可以呼叫Image 对象的isMutable()方法得知。我们可以使用getWidth()与getHeight()取得该Image 对象的宽度与高度。
要产生不可修改的Image 对象,主要方法有三种:
1. 从影像文件读取:根据MIDP 的规定,所实现MIDP 的厂商至少要提供读取PNG(Portable Network Graphics)影像文件的功能。有些厂商会支持其它如GIF 影像文件的功能,但是不建议使用,因为很可能让您的MIDlet 无法跨平台。
2. 由Byte 数组建立:我们可以经由从网络或resourcebundle 里的文字文件读入一连串的byte 数组,然后用此数组产生不可修改的Image 对象。
3. 从其它Image 对象(可修改或不可修改皆可)来产生。
范例:
import javax.microedition.lcdui.*;
public class ImageCanvas extends Canvas
{
private Image img;
/** Creates a new instance of ImageCanvas */
public ImageCanvas()
{
try
{
img = Image.createImage("/mario.PNG");
}
catch (Exception e)
{
e.printStackTrace();
}
}
public void paint(Graphics g)
{
g.drawImage(img, 0, 0, g.TOP|g.LEFT);
}
}
在此范例之中,我们使用:img = Image.createImage("/mario.PNG");
从MIDlet Suite 之中读取名为mario.PNG的图片。
然后利用g.drawImage(image, 0, 0, g.TOP|g.LEFT);画出此Image对象。第二种建立不可修改Image 对象的方法是利用其方法:
createImage(byte[] imagedata, int imageOffset, int imageLength)
第三种方法则是利用方法: createImage(Image source)就可以从现有可修改或不可修改的Image 对象取得一份不可修改的拷贝。
可修改的Image 对象
建立一个可修改的Image 对象非常简单,只要呼叫Image 对象的静态方法:
createImage(int width,int height)
即可建立一个可修改的Image 对象。事实上,可修改的Image和Double Buffering 的技术息息相关,可修改的Image 对象实际上就是一个供人在背景绘图的off screen。因此在建立可修改的Image 对象前,您应该先呼叫Canvas 类别的isDoubleBuffered()函式来确定您的机器是否支持Double Buffering 技术,如果该函式传回false,那么您就不该试图建立可修改的Image 对象。
一旦您取得了可修改的Image 对象,我们就可以呼叫Image 类别的getGraphics()取得代表off screen 的Graphics 对象,在off screen 绘图并不会影响到正常的画面(on screen)。
最后,我们可以利用on screen ( 由paint 函式传入的Graphics 物件) 的drawImage()函
式绘出可修改Image 对象的内容。
示例:
Image source;
source = Image.createImage(“... ”) ;
//建立可修改Image对象
Image copy = Image.createImage(source.getWidth(), source.getHeight());
Graphics g = copy.getGraphics();//获取copy的Graphics对象
g.drawImage(source, 0, 0, Graphics.TOP|Graphics.LEFT);
5.9 绘制图片、文字以及定位点的作用
绘制图片、字符串或是单一文字都会用到定位点(Anchor)的概念。定位点代表的意义是,绘制图形跟文字时,所指定的X、Y坐标指的是何种意义。
定位点定义共有七种:
Graphics.TOP
Graphics.BOTTOM
Graphics.LEFT
Graphics.RIGHT
Graphics.HCENTER
Graphics.VCENTER
Graphics.BASELINE它们对文字和图形的控制都具有意义。
注意:图中标有VCENTER参数,主要是因为MIDP1.0提供了该参数。但MIDP2.0中已经不允许使用该参数,主要是因为这个参数不好计算而且实际使用的意义也不大,如果在MIDP2.0中调用该参数,将会抛出异常。
这几种定义可以有意义地组合。举例来说,如果我们选择使用TOP 加上LEFT,则绘制文字时,我们会使用函式:
g.drawString(“文字xyzh”, 0, 0, Graphics.TOP|Graphics|LEFT) ;
绘制图形时,我们会使用函式:
g.drawImage(image, 0, 0, g.TOP|g.LEFT);
这时画面上的结果为:
不管是drawString()或是drawImage()其第二与第三个参数所指定的坐标指的是定位点所参考的起始地址。以上述结果为例,我们指定(0,0)为定位点参考起始位置,然后又选择的TOP 与LEFT 作为定位点,代表(0,0)这个坐标为字符串或图形绘制在屏幕上时左上角的点。
再举个例子,如果我们选择使用BOTTOM 加上HCENTER,则绘制文字时,我们会使用函式:
g.drawString(“文字xyzh”, 0, 0,Graphics.BOTTOM|Graphics.HCENTER) ;
绘制图形时,我们会使用函式:
g.drawImage(image, 0, 0, g.BOTTOM |g.HCENTER);
这时画面上的结果为:
由此我们可以归纳出,如果您使用的方法为:
g.drawString("Hello",x,y,g.TOP|g.LEFT) ;

g.drawString("Hello",x,y,0) ;
跟我们使用
g.drawString("Hello",x + stringWidth()/2,y + getHeight(),g.BOTTOM|g.HCENTER) ;
两者的意义是相同。
思考练习:居中字符串。
5.10 字体
当我们需要在屏幕上汇出文字时,我们常常需要一些有关字体的相关数据,这时就需要Font 类别的辅助。通常,我们会使用Font.getDefaultFont()取得代表系统预设所使用字型的Font 对象。或者也可以自行使用Font.getFont()来取得代表特定字型的对象。
getFont() 共有三个参数,
Font f = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_LARGE) ;
他们分别是外观( face )、样式(style)、以及尺寸(size)。他们分别有各种选项:
外观: Graphics.FACE_MONOSPACE 定宽字体
Graphics.FACE_PROPORTIONAL 比例字体
Graphics.FACE_SYSTEM 系统字体
样式 : Graphics.STYLE_BOLD 加粗字体
Graphics.STYLE_ITALIC 倾斜字体
raphics.STYLE_PLAIN 常规字体
Graphics.STYLE_UNDERLINED 下划线
尺寸 : Graphics.SIZE_LARGE 大号字体
Graphics.SIZE_MEDIUM 中号字体
Graphics.SIZE_SMALL 小号字体
需要注意的是,底层不一定全部支持。getFont()还有另一个只有一个参数的重载方法,只有PONT_INPUT_TEXT和DON’T_STATIC_TEXT两种可以选择,这个方法用来取得系统用来显示输入用的字体以及一般常用的字体。
但是最实际程序之中,我们最常使用的是Graphics 类别的getFont()方法取得当时显示在画面上的屏幕所使用的字型。同理,我们也可以使用Graphics 类别的setFont()方法设定所使用的字体:
g.setFont(f);
当我们取得代表字型的对象之后,我们就可以利用getFace()函式取得其外观、getStyle()取得其样式、getSize()取得其尺寸。而样式的部分我们也可以改用isBold()、isItalic()、isPlain()、isUnderlined()函式来取得相关信息,这是因为样式是可以合成的,一个字型的样式很可能同时是黑体与斜体的组合。
Font 类别除了可以帮我们取得字型的外观、样式与尺寸之外,还能帮助我们取得该字型在屏幕上的相关属性,请参考下图:
Font 的属性
其中:
charWidth()取得屏幕上某个字符使用该字体显示时的宽度;
charsWidth()计算一串字符显示时的宽度;
stringWidth()取得屏幕上某个字符串使用该字体显示时的宽度;
substringWidth()则是某个字符串的子字符串显示时的宽度;
getBashLinePosition()可以让我们知道从字体最顶点到baseline的距离;
getHeight()可以取得最顶点到最低点的距离。
5.11 颜色
Grpahics类提供了3种颜色设置方法:
public void setColor(int r, int g, int b)
public void setColor(int RGB)
public void setGrayScale(int value)
其中,setGrayScale作用是绘制灰阶,只是0到255共256种;setColor方法的作用是设置RGB颜色模式以在屏幕上显示彩色的颜色。
RGB模型:对于彩色图像中的每个RGB(Red、Green、Blue)分量,为每个像素指定一个0(黑色)到255(白色)之间的强度值。当三个分量相等时,结果是中性灰色;当所有分量的值均为255时,结果为白色;当这些值都为0时,结果为纯黑色。
setColor(int RGB)的参数格式是0x00RRGGBB,高2位0x00被忽略,仅仅计算RRGGBB,高2位主要用来计算色彩的Aplpha混合的。
5.12 调整坐标原点
Graphics类提供了调整屏幕原点位置的方法法:
public void translate(int x, int y)
改变原点的坐标在手机屏幕上的位置,并把相对于原来坐标系统原点的坐标(x,y)作为新的原点。需要注意的是,每次调用translate方法,都是针对当前的坐标系统原点进行调整的,并不是以屏幕左上角进行调整的。
例如:当前坐标为(0,0),如果调用了translate(2,3)则当前原点坐标为原来屏幕的(2,3)坐标,如果再调用translate(4,5),则坐标(4,5)是相对于坐标(2,3)的,所以相对于最原始的坐标系统中的坐标(6,8)。
一定要注意每次转移原点都是根据当前屏幕的原点坐标为标准的。
Grphics还提供了2个计算目前坐标与原始状态下原点坐标的距离的方法。它们是: getTranslateX()和getTranslateY()。
因为一个坐标系统可以进行多次坐标原点的转移,如果希望原点恢复到原始状态,只要再转移一个负变量就是原点往坐标轴的负方向移动,可以使用如下代码恢复原始状态的圆点位置:
g.translate(-getTranslateX(), -getTranslateY()) ;
如果希望在已经转移了原点的环境中使用绝对位置(ax,ay),绝对位置就是指相对于原始状态的原点位置:
g.translate(ax – getTranslateX(), ay – getTranslateY());
5.14 裁剪区
在处理图像时,经常会碰到图片比较大,而屏幕只能显示图像一部分的情况。因此需要确定图像中哪些部分位于显示区域内,而哪些内容落在显示区域之外,以便只显示落在显示区域内的那部分图像。这个选择的过程成为裁剪。
裁剪区的作用:只有在裁剪区域内的绘图过程才会真正有效,在区域外的无效,即使在区域外之行了绘图方法也是不会显示的。
Graphics类提供了简单的裁剪方法,原形如下:
public void setClip(int x, int y, int width, int height)
setClip可以设置需要裁剪的开始坐标(x,y),然后指定矩形的裁剪区域的宽和高。当屏幕重绘的时候,可以保证屏幕的其它部分的内容不必重绘,仅仅重绘需要更新的部分内容。一般使用了裁剪方法以后,应该恢复到原来的裁减区域。
例:
int oldClipX = g.getClipX();
int oldClipY = g.getClipY();
int oldClipWidth = g.getClipWidth();
int oldClipHeight = g.getClipHeight();
//设置裁减区域
g.setClip( 50, 50, 100, 100);
//恢复原来的裁减区域
g.setClip(oldClipX, oldClipY, oldClipWidth, oldClipHeight);
5.15 重绘机制
当需要重绘屏幕的时候,可以调用Canvas类的repaint()方法,然后程序会自动调用paint()方法完成重绘,如果仅仅需要屏幕的一部分更新,可以使用repaint方法的一个重载方法,指定需要更新区域的坐标以及宽度和高度,请求部分屏幕重绘, 例如:
repaint(10, 10, 50, 50);
重绘起始坐标为(10, 10),宽度为50, 高度为50的屏幕区域。
5.16 双缓存技术
双缓存技术是计算机动画的一项传统技术。
屏幕闪烁的主要原因在于:一幅画面正在显示的时候,程序在改变它,然后在一幅图片还没有完全显示完毕又请求重新绘制,于是表现为画面闪烁。
解决闪烁的办法:在内存中操作需要处理的图片,程序对内存中的图片更新、修改、完成后再显示它。这样显示出来的图片永远是完全画好的图像,程序修改的将不是正在被显示
的图像。
5.17 动画制作
基本的动画,是将显示在屏幕上的角色等的动作与描绘的位置作连续的变化,产生动态的效果。


<转载:http://blog.csdn.net/allan_sun/archive/2006/08/09/1043157.aspx >

posted on 2008-09-01 19:43 骑猪闯天下 阅读(1291) 评论(0)  编辑  收藏


只有注册用户登录后才能发表评论。


网站导航: