引自http://blog.csdn.net/firefight/archive/2007/02/06/1503103.aspx
使用 Batik 创建 SVG 应用程序
Batik 工具集提供的 JSVGCanvas 模块是一个 swing 组件,用于显示静态或动态 SVG 文档。通过 JSVGCanvas 模块,开发人员可以轻松显示 SVG 文档(通过 URI 地址或 DOM 树)并对其进行操作,例如旋转、缩放、摇动、选择文本或**超级链接等。首先介绍如何创建 JSVGCanvas 并集成到一个 swing 应用程序中。接下来解释如何完成与 SVG 画布相关的常用功能,例如如何跟踪 SVG 文档渲染时发生的所有事件,以及如何通过 JavaTM 语言操作 SVG 文档。
创建 JSVGCanvas
JSVGCanvas 是一个 swing 组件并遵循 swing 设计规则 (Swing design rule [4] ) 。这意味着组件不是线程安全的,而且所有操作应当参照 swing 教程描述使用。 JSVGCanvas 也是一个 JavaBean 组件,因此可以在可视化应用程序开发工具中使用。下例中演示了如何轻松创建和使用 JSVGCanvas 组件(见图 3 )。
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import org.apache.batik.swing.JSVGCanvas;
import org.apache.batik.swing.gvt.GVTTreeRendererAdapter;
import org.apache.batik.swing.gvt.GVTTreeRendererEvent;
import org.apache.batik.swing.svg.SVGDocumentLoaderAdapter;
import org.apache.batik.swing.svg.SVGDocumentLoaderEvent;
import org.apache.batik.swing.svg.GVTTreeBuilderAdapter;
import org.apache.batik.swing.svg.GVTTreeBuilderEvent;
public class SVGApplication {
public static void main(String[] args) {
JFrame f = new JFrame("Batik");
SVGApplication app = new SVGApplication(f);
f.getContentPane().add(app.createComponents());
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
f.setSize(400, 400);
f.setVisible(true);
}
JFrame frame;
JButton button = new JButton("Load...");
JLabel label = new JLabel();
JSVGCanvas svgCanvas = new JSVGCanvas();
public SVGApplication(JFrame f) {
frame = f;
}
public JComponent createComponents() {
final JPanel panel = new JPanel(new BorderLayout());
JPanel p = new JPanel(new FlowLayout(FlowLayout.LEFT));
p.add(button);
p.add(label);
panel.add(p, BorderLayout.NORTH);
panel.add(svgCanvas, BorderLayout.CENTER);
// Set the button action.
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
JFileChooser fc = new JFileChooser(".");
int choice = fc.showOpenDialog(panel);
if (choice == JFileChooser.APPROVE_OPTION) {
File f = fc.getSelectedFile();
try {
svgCanvas.setURI(f.toURL().toString());
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
});
// Set the JSVGCanvas listeners.
svgCanvas.addSVGDocumentLoaderListener(new SVGDocumentLoaderAdapter() {
public void documentLoadingStarted(SVGDocumentLoaderEvent e) {
label.setText("Document Loading...");
}
public void documentLoadingCompleted(SVGDocumentLoaderEvent e) {
label.setText("Document Loaded.");
}
});
svgCanvas.addGVTTreeBuilderListener(new GVTTreeBuilderAdapter() {
public void gvtBuildStarted(GVTTreeBuilderEvent e) {
label.setText("Build Started...");
}
public void gvtBuildCompleted(GVTTreeBuilderEvent e) {
label.setText("Build Done.");
frame.pack();
}
});
svgCanvas.addGVTTreeRendererListener(new GVTTreeRendererAdapter() {
public void gvtRenderingPrepare(GVTTreeRendererEvent e) {
label.setText("Rendering Started...");
}
public void gvtRenderingCompleted(GVTTreeRendererEvent e) {
label.setText("");
}
});
return panel;
}
}
图
3:
运行中的
SVG
应用程序
事件处理机制
如上例所示,每当设置 JSVGCanvas 的 URI 或 SVG DOM 时(通过 setURI 或 setSVGDocument 方法),相关文档首先被解析(在 URI 的情况),然后创建、渲染和有选择的更新。为了接收这些不同的阶段的事件通知,正确的方法是实现一个侦听器并附加到该组件。有五种类型的侦听器:
- SVGDocumentLoaderListener – 提供了用于跟踪 SVGDocumentLoaderEvent 事件的一组方法。它描述了SVG装载阶段,即使用 SVG 文件创建 SVG DOM 树。
- GVTTreeBuilderListener – 提供了用于跟踪 GVTTreeBuilderEvent 事件的一组方法。它描述了创建阶段,即通过 SVG DOM 树创建一个 GVT (图形矢量工具集),然后 GVT 树 被用于渲染文档。
- SVGLoadEventDispatcherListener – 提供了用于跟踪 SVGLoadEventDispatcherEvent 事件的一组方法。它描述了 DOM 'SVGLoad' 的事件派发阶段。该事件仅在动态类型文档中触发。
- GVTTreeRendererListener – 提供了用于跟踪 GVTTreeRendererEvent 事件的一组方法。它描述了渲染阶段,即使用一个 GVT 树创建图像。在动态文档中该事件只在最初渲染时被触发一次。
- UpdateManagerListener – 提供了用于跟踪 UpdateManagerEvent 事件的一组方法。它描述了运行阶段,即显示 更新管理器启动,然后显示线程可能被挂起、恢复或停止。该侦听器可用于跟踪图像更新情况。只有动态文档触发该事件。
使用 JavaTM 脚本
Batik 工具集提供了简便的,基于 JavaTM 语言的 SVG 文档脚本。在前一节中,我们学习了如何显示一个 SVG 文档;本节描述如何操作当前在 JSVGCanvas 中显示的 SVG 文档。下面的例子中演示了如何操作 SVG 文档。注意开发人员不需要考虑图像的更新问题,在事件侦听器**后画布根据需要进行自动更新。
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import org.apache.batik.swing.JSVGCanvas;
import org.apache.batik.swing.svg.SVGLoadEventDispatcherAdapter;
import org.apache.batik.swing.svg.SVGLoadEventDispatcherEvent;
import org.apache.batik.script.Window;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;
public class SVGApplication {
public static void main(String[] args) {
new SVGApplication();
}
JFrame frame;
JSVGCanvas canvas;
Document document;
Window window;
public SVGApplication() {
frame = new JFrame();
canvas = new JSVGCanvas();
// Forces the canvas to always be dynamic even if the current
// document does not contain scripting or animation.
canvas.setDocumentState(JSVGCanvas.ALWAYS_DYNAMIC);
canvas.addSVGLoadEventDispatcherListener
(new SVGLoadEventDispatcherAdapter() {
public void svgLoadEventDispatchStarted
(SVGLoadEventDispatcherEvent e) {
// At this time the document is available...
document = canvas.getSVGDocument();
// ...and the window object too.
window = canvas.getUpdateManager().
getScriptingEnvironment().createWindow();
// Registers the listeners on the document
// just before the SVGLoad event is dispatched.
registerListeners();
// It is time to pack the frame.
frame.pack();
}
});
frame.addWindowListener(new WindowAdapter() {
public void windowOpened(WindowEvent e) {
// The canvas is ready to load the base document
// now, from the AWT thread.
canvas.setURI("doc.svg");
}
});
frame.getContentPane().add(canvas);
frame.setSize(800, 600);
frame.show();
}
public void registerListeners() {
// Gets an element from the loaded document.
Element elt = document.getElementById("elt-id");
EventTarget t = (EventTarget)elt;
// Adds a 'onload' listener
t.addEventListener("SVGLoad", new OnLoadAction(), false);
// Adds a 'onclick' listener
t.addEventListener("click", new OnClickAction(), false);
}
public class OnLoadAction implements EventListener {
public void handleEvent(Event evt) {
// Make some actions here...
// ... for example start an animation loop:
window.setInterval(new Animation(), 50);
}
}
public class OnClickAction implements EventListener {
public void handleEvent(Event evt) {
// Make some actions here...
// ... for example schedule an action for later:
window.setTimeout(new DelayedTask(), 500);
}
}
public class Animation implements Runnable {
public void run() {
// Insert animation code here...
}
}
public class DelayedTask implements Runnable {
public void run() {
// Make some actions here...
// ... for example displays an alert dialog:
window.alert("Delayed Action invoked!");
}
}
}
在 SVG 文档中注册的 DOM 侦听器在画布更新线程中被**。为了避免冲突的情况,开发人员不应该在另一个线程中操作 DOM 树,而应当切换到画布更新线程进行更新操作。从外部线程切换到画布更新线程的方法如下:
// Returns immediately
canvas.getUpdateManager().getUpdateRunnableQueue().
invokeLater(new Runnable() {
// Insert some actions on the DOM here
});
// Waits until the Runnable is invoked
canvas.getUpdateManager().getUpdateRunnableQueue().
invokeAndWait(new Runnable() {
// Insert some actions on the DOM here
});
与常规的事件侦听器相似,当 Runnable 从更新线程**时,图形被更新。
Batik 同时提供了 SVG<script> 元素的扩展,以便在 SVG 文档中执行 Java 程序。所有使用 bridge 模块的 Batik 应用程序都可以使用该功能(例如 JSVGCanvas 和 ImageTranscoder 模块)。为了使用该扩展, <script> 元素中的 'type' 属性必须设置为 application/java-archive 。另外, xlink:href 属性应设置为执行程序所在的 jar 文档 URI 地址。该 jar 文件的表述文件( manifest )必须包括下表中的入口项:
Script-Handler: <classname>
<classname> 必须是实现 org.apache.batik.script.ScriptHandler 接口的类的名称。该类可以直接放在 jar 文件中,也可以位于表述文件 Class-Path 入口项所包含的其它 jar 文件中。
总结
在本文中,我们了解到开发人员如何使用 Batik 工具集创建、操作和显示 SVG 内容。 Batik 的模块具有良好的扩展性而且易于使用,通过本文的学习, JavaTM 开发人员现在可以着手编写客户端或服务器端的 SVG 应用程序。另外, Batik 项目是 Apache 软件基金会 (ASF) 倡导的开源志愿项目。这就意味着有很多方式对该项目贡献自己的力量,包括直接参与(编码、写文档、回答问题、提供想法、报告错误、错误修改建议等等),或资源捐献(公开代码、硬件、软件、出席会议、演讲等)。本项目特别关注使用 Batik 模块的应用程序,包括各种工具和扩展,因此请积极的通过 Batik 邮件列表为本项目作出贡献,邮件列表为 [email protected] 。
参考资料
[1] "The official SVG page at W3C" ,
SVG 工作组,网址 http://www.w3.org/Graphics/svg .
[2] "The Document Object Model" ,
DOM 工作组,网址 http://www.w3.org/DOM .
[3] "The Batik SVG Generator Tutorial" ,
Batik 小组,网址 http://xml.apache.org/batik/svggen.html .
http://www.svgopen.org/2002/papers/kormann__developing_svg_apps_with_batik/