基于J2ME的SVGT移动应用
摘要
本文首先介绍了矢量图形在移动领域中具有的优势,然后对其相关技术,包括
Mobile SVG、J2ME以及JSR-226和JSR-172进行简单的介绍。最后,在以上技术的支持下,创建一个简单基于J2ME的SVGT网络监控应用。
矢量图形在移动应用上的优势
矢量图形与光栅格式图像相比,在对动画、地图和互动图形进行编码和显示方面的优势是明显的。矢量图形是动态的、可以缩放的,能够描述非常高级的图形特性,如复杂形状、动画、分层图形和特殊效果等。
利用矢量图形的缩放性,图形可以调整大小,以适应任何显示设备而不会导致品质损失。这在移动设备上是一个优势,因为移动设备的显示屏的形状、尺寸和分辨率差别很大。矢量图形还可以很方便的进行平移、缩放、旋转以及与用户交互的操作,因此用户可以在不影响图形质量的情况下对图形进行放大,这在使用手机的小屏幕看图时特别有用。
矢量图形文件通常小于光栅图像文件,从而可以缩短无线下载时间,这点对于非常计较带宽的移动应用来说尤其重要。另外,将当前屏幕上的图形放大时,对于光栅图像会出现使图像模糊的马赛克效应,此时若要获得高质量的放大图像,则需重新从服务器获取放大后的图像,从而增加了网络的流量,而矢量图形在客户端进行放大就可以得到没有质量损失的放大图形。
矢量图形的另一个强大功能是可以存储图形中各元素的相关信息。例如,通过对建筑矢量图形的附加数据,就可以知道房间的面积,售价等信息。
最后,矢量图形可以方便的利用搜索引擎对图形中的属性进行搜索,实现基于图形的数据搜索。
目前在移动领域的矢量应用主要包括
FlashLite和Mobile SVG等,本文主要讨论Mobile SVG。
Mobile SVG
标准
SVG(Scalable Vector Graphics,可缩放矢量图像)是互联网联盟(W3C)的正式推荐标准,它是一种使用XML来描述二维图像的语言。由于SVG的大部分特性非常适合于无线领域的图形应用,为了满足移动业界的需求, W3C的SVG工作小组制订了适合于移动应用领域的Mobile SVG标准。
Mobile SVG主要用于各种资源非常有限的移动设备,所以在实现Mobile SVG时,性能指标成为最主要的指标。由于移动设备在CPU速度、内存大小、支持的显示颜色等各个参数上有很大的不同,单一的专业标准很难满足所有移动设备的要求。因此,为了覆盖不同移动设备家族的需求,SVG工作小组最终制订了两个级别的Mobile SVG专业标准。一种专业标准是SVG Tiny (SVGT),适用于资源高度受限的移动设备,如手机;另一种专业标准是SVG Basic (SVGB),适用于高端的移动设备,如PDA等。由于移动设备硬件条件的限制,相对于标准的SVG,Mobile SVG在支持的内容、属性、功能等方面作了限制。SVGB是标准SVG的子集,而SVGT又是SVGB的子集,SVGT标准中删除了透明、渐变、裁剪、图案、符号和蒙板等复杂功能,而且没有对脚本支持。具体的不同请参考W3C SVG网址。
以下是一个
SVGT的例子:
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:demo="http://www.sun.com/svg/demo"
id="root"
width="100%" height="100%" viewBox="0 0 240 320"
xml:space="preserve"
preserveAspectRatio="none">
<rect fill="#35556B" width="240" height="320"/>
<g id="processingIndicator" transform="translate(120, 97) scale(0.5)" >
<g id="processingIndicator.outerCircle">
<path transform="translate(-120, -97)" fill="#C1CBD1" d="M73.426,97.169c0,25.681,20.894,46.575,46.575,46.575c25.681,0,46.574-20.894,46.574-46.575c0-9.251-2.708-18.192-7.83-25.854c-1.688-2.525-5.104-3.204-7.629-1.516s-3.204,5.104-1.516,7.629c3.908,5.847,5.975,12.673,5.975,19.741c0,19.616-15.959,35.575-35.574,35.575c-19.616,0-35.575-15.958-35.575-35.575c0-19.616,15.959-35.575,35.575-35.575c6.063,0,12.048,1.551,17.307,4.485c2.652,1.48,6.002,0.53,7.482-2.123c1.48-2.653,0.529-6.003-2.123-7.483c-6.893-3.846-14.73-5.879-22.666-5.879C94.319,50.594,73.426,71.488,73.426,97.169z"/>
</g>
<g id="processingIndicator.innerCircle">
<path transform="translate(-120, -97)" fill="#738999" d="M120.001,69.772c-5.441,0-10.701,1.592-15.21,4.607c-2.525,1.688-3.204,5.104-1.516,7.629c1.688,2.525,5.104,3.204,7.629,1.516c2.693-1.8,5.838-2.752,9.096-2.751c4.38,0,8.498,1.705,11.594,4.802c3.098,3.097,4.803,7.214,4.803,11.594c0,9.042-7.355,16.397-16.397,16.397c-9.041,0-16.396-7.355-16.396-16.397l0.065-1.479c0.134-3.034-2.201-5.848-5.236-5.981c-3.034-0.134-5.587,1.972-5.721,5.007l-0.108,2.454c0,15.107,12.29,27.397,27.396,27.397c15.107,0,27.397-12.291,27.397-27.397c0-7.318-2.85-14.198-8.024-19.373S127.318,69.772,120.001,69.772z"/>
</g>
<g id="processingIndicator.center" >
<circle fill="white" transform="translate(-120, -97)" cx="120" cy="97.169" r="9.349"/>
<circle display="none" fill="#35556B" transform="translate(-120, -97)" cx="122.321" cy="92.551" r="2.078"/>
</g>
</g>
<defs>
<animateTransform id="processingIndicatorAnim" xlink:href="#processingIndicator.innerCircle" attributeName="transform" type="rotate" values="0;360" begin="0s" dur="3s" repeatDur="indefinite"/>
<animateTransform xlink:href="#processingIndicator.outerCircle" attributeName="transform" type="rotate" values="0;-360" begin="0s" dur="2s" repeatDur="indefinite"/>
<animateTransform xlink:href="#processingIndicator.center" attributeName="transform" type="rotate" values="0;-360" begin="0s" dur="1s" repeatDur="indefinite"/>
</defs>
<g id="loadProgressBar">
<rect id="loadProgressBar.bkg" fill="#A3B8CB" x="45" y="250" width="150" height="10" />
<rect id="loadProgressBar.progress" fill="white" x="45" y="250" width="20" height="10" />
<text transform="translate(120,230)" fill="#BDBEC0" font-size="12" text-anchor="middle">loading application</text>
<text id="loadProgressBar.text" text-anchor="middle" transform="translate(120,275)" fill="#FFFFFF" font-size="12">0%</text>
</g>
</svg>
Mobile SVG最近已被
3GPP组织所采纳,用于多媒体短信服务(MMS)。Mobile SVG将很快应用在许多2.5G和3G MMS服务中。Mobile SVG的增强MMS短信功能对于手机制造商具有明显的吸引力。与运营商一样,OEM厂商也可以在不对设计作重大变动或不显著增加成本的情况下,增加 Mobile SVG功能。由于Mobile SVG是内容丰富的通用平台,最适合于帮助设备制造商提供差别化服务。
为了实现
SVGT在手机上的应用,开发人员需要了解手机开发平台及其对SVGT的支持情况,其中最常见的是基于JAVA语言的J2ME移动开发平台。
J2ME
应用及其规范扩展
随着移动技术的发展,手机已经从最初的一种单纯的通信工具转变成如今集通信、工作、娱乐等功能为一体的综合设备。因此,在移动终端上开发通用的、丰富的应用已成为必然的趋势。这些应用能够提供与桌面应用相媲美的功能,并可以按用户的意愿随时安装和删除。然而手机平台在硬件,操作系统等系统底层有很大的不同,给应用程序的开发带来了很大的困难,开发人员迫切需要一种通用的开发平台。
J2ME(
JAVA2 Micro Edition)正是这样一种JAVA应用开发平台。实际上,JAVA语言从其诞生起就以其运行的平台无关性这一强大的优势而成为网络应用的宠儿。J2ME是JAVA2标准版本的微型版本,专门为小型移动设备所设计。这些设备处理器的处理能力都不强,可使用的资源也有限。因此,J2ME只包含了J2SE中在移动通信设备上所必需的功能和组件,使其能够在移动设备及其有限的资源上开发出丰富多彩且平台无关的应用。J2ME在结构上分为CDC(Connected Device Configuration)和相应的Foundation Profile规范,以及CLDC(Connected Limited Device Configuration)和相应的MIDP规范。
MIDP(
Mobile Information Device Profile)是移动信息设备规范的简称,规范具体定义了J2ME适用的硬件和软件框架,并提供了这个框架要实现的基本功能及其标准接口,应用开发者可以基于这个框架开发出各种应用。2000年9月,SUN公司发布了MIDP的第一个正式版本MIDP1.0。它将J2ME适用的设备定位在至少拥有数百KB RAM和ROM,并具有基本网络和显示功能的移动通信设备上。在该基础上定义了一系列软件接口,其中包括基本输入输出、图形化用户接口(GUI)、网络、事件机制、文件系统、应用管理系统(AMS)等。之后,随着JAVA技术的不断发展和用户需求的不断提高,SUN公司又于2002年11月发布了MIDP2.0。它对设备的内存资源和处理能力的要求较1.0要高,但也为应用开发者提供了更方便、更丰富多彩的软件包。MIDP2.0中主要增加了游戏接口的实现、声音输出接口的实现安全网络机制的实现。MIDP2.0的这些特性将使基于移动设备的JAVA应用具有更加广阔的前景,也必将使新一代的移动设备发生革命性的变化并领导时尚潮流。
另外,为了支持不同的应用方向,
SUN还联合其它一些公司共同制定了J2ME扩展技术规范,包括图形应用、网络服务、多媒体和安全等。其中本文中主要使用到移动2D SVG图形方面的JSR-226和WEB服务方面的JSR-172。
所有
MIDP应用从MIDlet类中派生,MIDlet类管理和控制着应用程序的生命周期,包括装载、激活、暂停和销毁四个阶段。当设备装入MIDlet并调用其构造函数时为装载期,直到程序管理器调用应用程序的startApp()方法。在startApp()调用后,应用程序处于激活期,直到调用了pauseApp() 或 destroyApp() 。pauseApp() 暂停应用程序,而destroyApp()销毁应用程序。一个典型的MIDlet代码如下:
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class Hello extends MIDlet
{
private Display display;
public Hello()
{
display = Display.getDisplay(this);
}
public void startApp()
{
Form f = new Form("Test");
f.append("hello world!");
display.setCurrent(f);
}
public void pauseApp()
{
}
public void destroyApp(boolean unconditional)
{
}
}
J2ME
开发平台
开发人员可以在SUN的J2ME网址上下载CLDC移动开发工具,目前的最新版本为2.5。Sun Java™ Wireless Toolkit 2.5X新增了一个新的平台,移动服务架构 (JSR 248以及9个新的可选API包,其中包括:)
l Security and Trust Services APIs for J2ME (
JSR 177支持智能卡访问和数据加密功能,包括创建和签署证书。)
l Location API for J2ME (
JSR 179提供与
GPS等位置服务硬件交互功能。)
l SIP API for J2ME (
JSR 180实现了建立多媒体会话使用的标准会话启用协议(
Session Initiation Protocol)。)
l Content Handler API (
JSR 211提供内容处理机制,当接收到特定媒体类型的内容时自动启用相关的处理器。)
l Scalable 2D Vector Graphics API for J2ME (
JSR 226提供了显示和渲染
SVG内容的API。 )
l Payment API (
JSR 229为应用程序提供了标准的支付方式。)
l Advanced Multimedia Supplements (
JSR 234在
Mobile Media API (
JSR 135的基础上进行扩展,提供了3D音频,音频特效和照相支持。))
l Mobile Internationalization API (
JSR 238为多语言应用程序定义了资源文件机制。)
l Java Binding for the OpenGL® ES API (
JSR 239为
OpenGL® for Embedded Systems (OpenGL® ES) 和 EGL 的API提供了JAVA语言的绑定。OpenGL® ES为3D图形的标准API, EGL为标准的平台接口层。二者都是Khronos Group (
http://khronos.org/opengles/开发的。 ))
开发工具中带有丰富的示例,便于开发人员的了解和学习。
JSR-226
技术规范和应用
JSR-226的定义了
SVG图形(Mobile 2D Graphics,M2G)API。其中SVG矢量图形是ScalableImage的实例,你可以通过静态的createImage()方法获得它们。SVGImage是ScalableImage的一个子类,它提供事件处理和底层DOM文档访问之间的联系。大多数情况下我们会使用这个类。例如如何从一个SVGT文件中创建一个
SVGImage
实例:
try{
InputStream imageStream = getClass().getResourceAsStream("/loadScreen.svg");
SVGImage svgImage = (SVGImage)SVGImage.createImage(imageStream, null);
} catch(Exception e) {
e.printStackTrace();
}
然后,创建
ScalableGraphics的一个实例:
ScalableGraphics sg = ScalableGraphics.createInstance();
最后,在
paint事件中显示SVG图形:
public void paint(Graphics g) {
// *** clear the display
g.setColor(255, 255, 255);
g.fillRect(0, 0, getWidth(), getHeight());
// *** render the SVG image
sg.bindTarget(g);
sg.setTransparency(1f);
svgImage.setViewportWidth(getWidth());
svgImage.setViewportHeight(getHeight());
sg.render(0, 0, svgImage);
sg.releaseTarget();
}
为了使渲染动画
SVG内容的普通情况更加方便,JSR-226还提供了SVGAnimator类。通过SVGAnimator可以创建和控制一个Canvas对象,这个对象自动处理屏幕更新,以播放动画事件和相应用户的操作。因此使用SVGAnimator不需要显式创建ScalableGraphics对象。例如:
try{
InputStream imageStream = SVGMobile.class.getResourceAsStream("/Halloween.svg");
svgImage = (SVGImage)SVGImage.createImage(imageStream, null);
svgAnimator = SVGAnimator.createAnimator(svgImage);
}
catch(Exception e) {
e.printStackTrace();
}
// Set to 10 fps (frames per second)
svgAnimator.setTimeIncrement(0.01f);
svgCanvas = (Canvas)svgAnimator.getTargetComponent();
svgImage.setViewportWidth(svgCanvas.getWidth());
svgImage.setViewportHeight(svgCanvas.getHeight());
// The SVG root element is used to reset the time on a stop operation.
doc = svgImage.getDocument();
svg = (SVGSVGElement)doc.getDocumentElement();
svgCanvas.setCommandListener(this);
// Hook-in key listeners to play, pause and stop the animation.
svgAnimator.setSVGEventListener(this);
svgAnimator.play();
state = STATE_PLAYING;
System.err.println("PLAYING...");
// *** grab a reference to the display
display = Display.getDisplay(this);
display.setCurrent(svgCanvas);
// *** set up the midlet menu
int hotKey = 0;
svgCanvas.addCommand(exitCommand);
在应用程序中,不仅可以从
SVG文档装载静态的SVG数据,而且可以创建空的SVGImage,然后通过操作其
DOM数据形成需要的图形,下例中创建了一个空SVGImage,然后在
DOM树中追加了一个SVG text元素。
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.m2g.ScalableGraphics;
import javax.microedition.m2g.SVGImage;
import org.w3c.dom.Document;
import org.w3c.dom.svg.SVGElement;
import org.w3c.dom.svg.SVGSVGElement;
import org.w3c.dom.svg.SVGRGBColor;
public class HelloSVG extends MIDlet {
protected SVGImageCanvas svgCanvas = null;
public HelloSVG() {
}
public void startApp() {
SVGImage svgImage = SVGImage.createEmptyImage(null);
Document doc = svgImage.getDocument();
SVGSVGElement svg = (SVGSVGElement) doc.getDocumentElement();
SVGElement textElement = (SVGElement) doc.createElementNS("http://www.w3.org/2000/svg", "text");
textElement.setTrait("#text", "Hello JSR-226 !");
textElement.setFloatTrait("x", 50.0f);
textElement.setFloatTrait("y", 50.0f);
SVGRGBColor textColor = svg.createSVGRGBColor(0, 0, 0);
textElement.setRGBColorTrait( "stroke", textColor);
svg.appendChild(textElement);
svgCanvas = new SVGImageCanvas(svgImage);
Display.getDisplay(this).setCurrent(svgCanvas);
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
}
}
class SVGImageCanvas extends Canvas {
protected SVGImage svgImage;
protected ScalableGraphics sg = ScalableGraphics.createInstance();
protected SVGImageCanvas(final SVGImage svgImage) {
this.svgImage = svgImage;
}
public void paint(Graphics g) {
g.setColor(255, 255, 255);
g.fillRect(0, 0, getWidth(), getHeight());
sg.bindTarget(g);
svgImage.setViewportWidth(getWidth());
svgImage.setViewportHeight(getHeight());
sg.render(0, 0, svgImage);
sg.releaseTarget();
}
}
这些类都定义在
javax.miroedition.m2g和org.w3c.dom.svg包中,更多内容请参考JSR-226的技术规范文档。SVG DOM是在org.w3c.dom和org.w3c.dom.events包中定义的。