在 Kuix 框架下绘制自己定义的画布
本文详细介绍了如何在以低级界面实现的 Kuix 框架下绘制自定义画布,以及自定义画布用户事件的加入。
我们知道,Kuix 的实现全部是低级界面,也就是说你看到的每一个按钮,每一个输入框,等等貌似 JavaME 高级界面的东西,实际上都是利用 JavaME 的低级界面,也就是 Canvas 绘制而成。org.kalmeo.kuix.core.KuixCanvas 继承自 JavaME 的 javax.microedition.lcdui.game.GameCanvas,担当了绘制 Kuix 所有界面的重任。
这样就给我们绘制自己的低级界面带来了难度,有种把两只脚绑在一块走路的感觉。既然所有的界面都是用继承了 Canvas 的 KuixCanvas 来绘制,怎么可以再自定义一个 Canvas 的子类呢?除非脱离 Kuix 框架。但是有时候还必须得绘制自定义的低级界面。怎么办呢?——利用 Widget。 org.kalmeo.kuix.widget.Widget 是所有的 Kuix 下 widget 的基类,其作用和地位类似于 Qt 编程中的 QWidget。查看 org.kalmeo.kuix.widget.Widget 的 API,会发现它是有一个 public void paint(Graphics arg0) 方法的。原来 Kuix 的设计者们已经考虑到了我们的需求,并且给我们保留了一个进行自定义界面的接口。说做就做。写一个继承自 Widget 的类,画一个我们熟悉的绘制的低级界面:一个字符串“Hello,Kuix”和一个图片。源代码如下:
/**
* 文件名:PmapWidget.java
* 版本信息:
* 日期:2009-12-18
* Copyright XXX Corporation 2009
* 版权所有
*/
package com.xxx.kuix.widget;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import org.kalmeo.kuix.widget.Widget;
import org.kalmeo.util.resource.ImageManager;
/**
*
* 项目名称:CanvasDemo2
* 类名称:PmapWidget
* 类描述:为了使低级界面(即画布)嵌入 Kuix,本类继承自 Kuix 的 Widget
* 类说明:针对抽象编程。本类作为所有低级界面的基类,尽可能多地拥有公共的代码,提高代码复用性
* 创建人:Defonds
* 创建时间:2009-12-18 上午11:12:39
* 修改人:Defonds
* 修改时间:2009-12-18 上午11:12:39
* 修改备注:
*/
public abstract class PmapWidget extends Widget {
//--------------------------------------------------------
//fields.
/**
* 图片的横坐标(abscissa)和纵坐标(ordinate)
*/
protected volatile int abscissa;
protected volatile int ordinate;
/**
* 用于在屏幕上显示的图片
*/
protected Image image;
//---------------------------------------------------
//methods.
/**
* 创建一个新的实例 PmapWidget.
*/
public PmapWidget(String tag) {
super(tag);
/**
* 图片初始化
*/
//image = Image.createImage("/img/rb.png");
image = ImageManager.instance.getImage("/img/rb.png");
/**
* 图片坐标初始化
*/
abscissa = 0;
ordinate = 0;
}
/**
* 重写 Widget 的 paint 方法
*/
public void paint(Graphics arg0){
/**
* 背景及其边框渲染
*/
paintBackground(arg0);
paintBorder(arg0);
/**
* 绘制字符串至屏幕
*/
arg0.setColor(255, 255, 255);
arg0.drawString("Hello,kuix", 20, 20, Graphics.TOP | Graphics.LEFT);
/**
* 绘制图片至屏幕
*/
arg0.drawImage(image, abscissa, ordinate, Graphics.TOP | Graphics.LEFT);
}
}
调用的时候,只需要写一个继承自这个类的子类即可。
Screen screen = new Screen();
PmapWidget map = new ChildPmapWidget("Child of widget");
screen.add(map);
screen.setCurrent();
效果图如下:
这时候我们会发现,虽然按照我们的意图绘制出了我们的低级界面,但是却只能显示,这不是我们想要的。我们要的是可以随我们的键盘/鼠标事件随意改变的低级界面。于是我们想到了 Canvas 的 keyPressed()/keyRepeated()/keyReleased()/pointerPressed()/pointerDragged()/pointerReleased() 方法。但是 widget 并非继承自 Canvas,如果非要加上这些方法,除非 PmapWidget 也继承 Canvas。我们知道:Java 所有的类只能有一个父类。也就是说这是不可行的。那么怎么样才可以加入这些方法来实现我们的功能呢。这时候有的朋友可能会想到:让 Widget 继承 Canvas!好主意。但是不可行。作者曾尝试过修改 Kuix 源码,也就是让 Widget 继承 Canvas,然后在 PmapWidget 中加入上面事件处理方法。但是事实证明:这样是不可行的,keyPressed 根本就不会起作用。读者如果感兴趣的话可以自己去试试。
那么为什么我们让 Widget 继承 Canvas,我们的按键事件处理方法还不起作用呢?这个问题就要从 Kuix 的事件处理机制谈起。关于 Kuix 的事件处理,网上相关文献很丰富,而且这里限于篇幅,作者不再详细介绍。我们只需要知道:Kuix 并没有在回调 Canvas 的系统主线程中处理事件,而是采取了将事件记录下来,使用工作线程处理和分发事件的方法。查看 org.kalmeo.kuix.core.KuixCanvas 的源代码,我们会发现,KuixCanvas 拦截了所有的用户事件(包括按键和指针)。比如拦截 keyPressed() 的源代码如下:
protected void keyPressed(int keyCode) {
processKeyEvent(KuixConstants.KEY_PRESSED_EVENT_TYPE, keyCode);
}
也就是说如果我们想相应用户事件,必须遵守 Kuix 的游戏规则,按照它的套路出招。Kuix 给我们提供了一个继承自 Widget 的 org.kalmeo.kuix.widget.ActionWidget
类,来进行自定义事件处理。于是我们的 PmapWidget 得到了改进:/**
* 文件名:PmapWidget.java
* 版本信息:
* 日期:2009-12-18
* Copyright XXX Corporation 2009
* 版权所有
*/
package com.xxx.kuix.widget;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import org.kalmeo.kuix.core.KuixConstants;
import org.kalmeo.kuix.widget.ActionWidget;
import org.kalmeo.util.resource.ImageManager;
/**
*
* 项目名称:CanvasDemo2
* 类名称:PmapWidget
* 类描述:为了使低级界面(即画布)嵌入 Kuix,本类继承自 Kuix 的 ActionWidget
* 类说明:针对抽象编程。本类作为所有低级界面的基类,尽可能多地拥有公共的代码,提高代码复用性
* 创建人:Defonds
* 创建时间:2009-12-18 上午11:12:39
* 修改人:Defonds
* 修改时间:2009-12-18 上午11:12:39
* 修改备注:
*/
public abstract class PmapWidget extends ActionWidget {
//--------------------------------------------------------
//fields.
/**
* 图片的横坐标(abscissa)和纵坐标(ordinate)
*/
protected volatile int abscissa;
protected volatile int ordinate;
/**
* 每按一下方向键,图片需要移动的象素
*/
protected final int pixels = 10;
/**
* 用于在屏幕上显示的图片
*/
protected Image image;
//---------------------------------------------------
//methods.
/**
* 创建一个新的实例 PmapWidget.
*/
public PmapWidget(String tag) {
super(tag);
/**
* 图片初始化
*/
image = ImageManager.instance.getImage("/img/rb.png");
/**
* 图片坐标初始化
*/
abscissa = 0;
ordinate = 0;
}
/**
* 重写 ActionWidget/Widget 的 processKeyEvent 方法
*/
public boolean processKeyEvent(byte type, int kuixKeyCode){
/**
* 捕捉用户按键类型以及按键键位并作出自定义相应
*/
switch(type){
case KuixConstants.KEY_PRESSED_EVENT_TYPE://-----按下事件
switch(kuixKeyCode){
case KuixConstants.KUIX_KEY_UP://-----------向上键键位
ordinate = ordinate + pixels;
break;
}
break;
case KuixConstants.KEY_REPEATED_EVENT_TYPE://-----连续按下事件
switch(kuixKeyCode){
case KuixConstants.KUIX_KEY_LEFT://------------向左键键位
abscissa = abscissa + pixels;
break;
}
}
return false;
}
/**
* 重写 Widget 的 paint 方法
*/
public void paint(Graphics arg0){
/**
* 背景及其边框渲染
*/
paintBackground(arg0);
paintBorder(arg0);
/**
* 绘制字符串至屏幕
*/
arg0.setColor(255, 255, 255);
arg0.drawString("Hello,kuix", 20, 20, Graphics.TOP | Graphics.LEFT);
/**
* 绘制图片至屏幕
*/
arg0.drawImage(image, abscissa, ordinate, Graphics.TOP | Graphics.LEFT);
}
}
再次运行程序,发现向上键按下的时候,屏幕已经开始作出相应了,效果图如下: