IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  Java technology  >

设计具有本机性能的跨平台 Java UI

使用 JNI 来访问 SLIK 皮肤接口

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


级别: 初级

Vladimir Silva (vsilva@us.ibm.com), OGSA 开发承包人, IBM

2004 年 3 月 01 日

您能编写跨多种平台编译、同时执行速度仍然像本机代码一样快的 Java 代码吗?这是一个困扰 Java 开发人员的难题,对于具有复杂 UI 的应用程序来说尤为如此。在本文中,开发人员 Vladimir Silva 建议了一种针对这个难题的有趣解决方案。您将学习如何使用 JNI 来访问 SLIK,后者是一个跨平台的 C API,它同时在 Windows 和 UNIX 上提供本机性能。

相对于诸如 C 和 C++ 这样的传统语言,Java 语言已主宰了中间层开发。Java 语言在开发社区得到普遍接受的原因是它的面向对象设计、平台无关性,以及丰富的支持 API。然而,它在设法将桌面作为一个真正的开发平台方面却很失败——或许这是由于使得它主宰中间层的相同原因吧。平台无关性意味着相对于本机 C 和 C++ 应用程序来说,性能会大大降低。

本文的目的是展示 Java 与简单皮肤接口(Simplistic Skin Interface,也称为 SLIK)的绑定(请参阅 参考资料,获取更多细节)。SLIK 是一个可用于构建跨平台 GUI 的 C API。通过使用 Java 本机接口(Java Native Interface,JNI),您可以将 Java 代码绑定到 SLIK,从而设计出像 C 和 C++ 一样执行的跨平台用户界面。

Java 语言和 C:动感二重唱

Java 技术支持与多种语言绑定,包括 C/C++、Fortran,等等。这一卓越特性为开发人员带来了无比的灵活性,允许他们编写需要某些操作系统相关功能的 API。

为了证明 SLIK 的高级用户界面功能,我将展示如何构建一个没有矩形窗口的 UI(也称为 皮肤带皮肤的窗口),它需要 Java 语言当前所没有提供的低级 API。Java 所提供的是使用 JNI 访问任何操作系统相关服务的手段。在本机端,C 是最佳的语言选择,而我将使用的 SLIK 的修改版本提供了实现本文目标的多平台 C API。

关于 JNI 的详细介绍超出了本文的范围;欲了解关于该主题的更多信息,请参考下面的 参考资料小节。务必要牢记的一件重要事情是,SLIK 是用 C 编写的,并且您需要 JNI 来从用 Java 语言编写的代码中访问它。





回页首


SLIK 概述

SLIK 是一个 C API,用于构建基于 WinAmp 风格的皮肤的高级用户界面。此类界面的例子包括像用于类 UNIX 系统的流行的 XMMS(X-Multimedia System,X-多媒体系统)和 XINE CD/DVD 播放器这样的音频和视频播放器。 使用这个 API 作为 GUI 的基础具有许多好处:

  • SLIK 包括一组增强的小部件,比如按钮、菜单、窗口、列表,以及当前的窗口工具包所没有提供的高分辨率图像。

  • 该 API 可用于多平台设计,因为它的核心库可同时在 Microsoft Windows 和类 UNIX 系统下编译。

  • SLIK 可以实现为一个 Windows DLL 或 UNIX 共享对象库,这意味着它可以跨多个应用程序重用,并且易于维护。

SLIK 软件由 GQmpeg 小组在 GNU 公共许可证(GNU Public License,GPL)之下提供。GQmpeg 小组提供的 SLIK 版本仅在类 UNIX 系统上作为可执行文件来编译。我创建了一个修改后的版本,它可以同时工作在 Win32 和 UNIX 系统上;这个版本以二进制的形式随本文的源代码一起提供(同时包括 Windows DLL 和 UNIX 共享对象或 SO 版本)。

我还同时提供了用于 UNIX 和 Windows 环境的 makefile。请参阅 参考资料小节,获得这些源代码下载。





回页首


结合使用 SLIK 和 Java 语言

您可以在 SLIK C 实现的基础上将 SLIK-JNI 接口层实现为 DLL 或 SO(取决于底层操作系统)。消息通过 JNI 层发送,从 C 层激发的回调层级联返回 Java 层。高级 Java JNI 类提供了针对低级层的主要访问点。这些类负责加载该二进制的 DLL/SO,并提供访问低级 C 子例程的接口。SLIK JNI 层是一个平台无关的组件,它充当 Java 代码和操作系统相关的代码之间的粘合剂。SLIK C API 是 SLIK 服务的具体操作系统相关的实现;它需要 GIMP 工具包(GIMP Toolkit,GTK)才能操作。(GTK 是 Linux 和许多 UNIX 版本使用的窗口管理器;Windows 用户必须安装 GTK 2.x 运行库才能使用 SLIK。请参阅本文的 参考资料,获得相关链接。)最后一个组件是皮肤 规格文件,这是一个包含诸如图像、标签、XY坐标等窗口小部件信息的配置文件。

所有这些层之间的交互如图 1 所示。


图 1. SLIK-Java 消息层
SLIK-Java 消息层




回页首


SLIK-Java 对象层次

图 2 中的 UML 图描绘了带皮肤的窗口对象层次和 JSLIK 包的本机层。JSLIK 对象层次的顶部是 GtkSkin 类。它封装了一个带皮肤的窗口,并为这样一个窗口提供以下有用功能:加载、卸载、最小化、图标化,等等。它还能够监听诸如鼠标移动、单击、子窗口的派生以及抛出异常等事件。它不同于常规窗口的地方在于,其几何形状和它们的所有子部件都基于从规格文件中加载的高分辨率图像。 GtkSkinNatives 类提供了调用实际绘制窗口的低级本机方法的接口。


图 2. 非矩形窗口对象层次
非矩形窗口对象层次

图 3 中的 UML 图描绘了可以使用 SLIK 来创建的各种窗口小部件的层次。


图 3. 小部件对象的 UML 图
小部件对象的 UML 图

这些窗口小部件封装了皮肤规格文件中定义的属性,该文件默认被命名为 skindata ,并且连同必要的图像文件一起位于皮肤目录中。窗口小部件的属性包括几何 XY 坐标、透明度,以及背景图像。清单 1 包含了节选自本文的示例皮肤之一的规格文件的内容。


清单 1. 皮肤规格文件节选
[main]
image = main-complete.png
transparent = FALSE
border = TRUE
border_left = 116
border_right = 63
[button_exit]
image = btn-12-exit.png
x = 252
y = 2
prelight = TRUE

欲了解关于这个文件的格式的完全描述,请参阅随本文的源代码提供的 SKIN-SPECS 文档。

SLIK 和 AWT/Swing

在构建 UI 时,务必记住 SLIK 接口是与 AWT(Abstract Window Toolkit,抽象窗口工具包)或 Swing GUI 工具包不兼容的。因而,您不能在同一个窗口中同时使用 AWT/Swing 小部件和 SLIK 小部件。然而,在 SLIK 窗口能够派生 AWT/Swing 窗口的意义上,SLIK 是可以和 AWT 或 Swing 共存的。不过,如果您遵循这个策略,应用程序的资源占用将会更多,因为您需要在系统上同时安装 AWT/Swing 和 GTK。





回页首


小部件概述

SLIK 提供了 WinAmp 风格的皮肤小部件,比如按钮、滑块、拨号盘、菜单、子窗口、标签和列表。正如您已经看到的那样,诸如图形、标签等小部件属性是从一个默认名为 skindata 的皮肤规格文件中读取的。其中一些小部件包括拨号盘旋钮(专门为多媒体播放器设计)、一个高度可定制的图形列表(通常用于播放列表)、菜单、按钮、标签和滑块。这些小部件的基本版本如图 4 所示。


图 4. 示例小部件
示例小部件

这些抽象小部件能够加载远远优于系统的默认窗口管理器的特定图形样式。然而,许多小部件没有在媒体播放器中广泛使用,它们当前也不是小部件集的组成部分。本文后面的 图 5给出了这些增强的图形样式的一个例子。该图描绘了一个具有许多美妙图形的类似 PDA 外观的应用程序。

这个 API 提供的完整属性集可从包括在本文代码包中的 SKIN-SPECS 文件中找到(请参阅 参考资料)。





回页首


用 Java 语言编写的一个示例皮肤客户机

SLIK 的 JNI 接口能够为 Java 客户机提供同时在 Windows 和 UNIX/Linux 环境中创建皮肤对话框的能力。像使用传统 GUI 工具包来构建的类一样,带皮肤的客户端将监听高级事件,比如窗口的关闭、最小化或最大化。然而,这个基于 SLIK 的类需要 SLIK 专用的接口来监听任何带皮肤的小部件的这些事件。清单 2 说明了这些接口。(以下所有清单都取自 SkinDemo.java,这是本文与本文配套的代码包的一部分。请 单击这里来获得这个文件的彩色语法显示的完整清单。)


清单 2 SLIK 事件接口
import jni.skin.slik.*;
import jni.skin.slik.evt.*;
import jni.skin.slik.widget.*;
import jni.skin.util.Debug;
public final class SkinDemo implements // listen for:
    WindowEventListener, // window evts: Window closed, mouse clicked, mouse dragged
	ButtonEventListener, // Button widget clicks
	ListEventListener,   // List evts: row clicked, etc...
	DialEventListener,   // Dial widget drags
	SliderEventListener, // Slider drags
	MenuEventListener    // Menu selection events
{
	...

C 层中生成的 GTK 小部件事件将通过 JNI 级联地返回到 Java 层。大多数小部件属性,比如标签、XY 位置以及图形,都是在皮肤规格文件中定义的。





回页首


加载皮肤数据文件

为了从磁盘加载皮肤规格文件,需要首先初始化 GtkSkin 类,然后注册您希望监听其事件的小部件,并加载该文件,如清单 3 所示。


清单 3. 从磁盘加载皮肤规格文件
/*
 * Main function
 */
public SkinDemo(String[] args) throws GtkSkinException
{
         ...
         // init gtk (create skin window) + JNI debug flag (TRUE/FALSE)
         skin = new GtkSkin("My App", GtkSkin.GTK_TRUE);
         // Init all widgets: for skin demo purposes (Widgets typically initialized when needed...)
         skin.initAllWidgets();
         // Create and register some widgets
         // See the SPEC file for details
         // example: [button_exit], [list_playlist], [number_song], ....
         // IMPORTANT: All widgets must exist in SPEC file
         // Widget data (imgs, lbls, etc. are defined in the spec file
         skin.registerWidget(new WText("title"), "Title goes here!");
         skin.registerWidget(new WButton("exit"));
         skin.registerWidget(new WButton("iconify"
         ...
         /**
         * Listen for Skin Window main evts: KeyPress, Mouse motion and Mouse BTN press
         */
         skin.addWindowEventListener(this);
         // load/show
         skin.loadSkin(skinPath);
         skin.show();
}

GtkSkin("My App", GtkSkin.TRUE) 调用将以调试模式初始化 GIMP 运行库。 之后,皮肤规格文件中定义的所有 widget 都必须注册,这样 Java 层才会监听诸如鼠标点击等事件。除了 widget 事件之外,应用程序还必须监听窗口事件,比如最小化、最大化和关闭窗口命令。最后, loadSkin(path) 方法调用将从磁盘读取一个给定的文件路径,而 show() 将显示该路径。





回页首


使用小部件:拨号盘、滑块和弹出菜单

拨号盘和滑块小部件对于设计诸如视频或音频播放器等多媒体应用程序的用户界面很有用。与所有 SLIK 小部件一样,诸如位置坐标、像素映射和字体等大多数属性都是在皮肤规格文件中定义的。Java 代码注册这些对象,以便从这些对象接收事件,如清单 4 所示。


清单 4. 拨号盘、滑块和弹出菜单
// dial widget
// see [dial_position] on the skin file for attributes
position = new WDial("position");
position.addDialListener(this);
skin.registerWidget(position);
// More documentation can be found on the SKIN-SPECS file
// in the src distribution of this article
// slider test: maps to [slider_volume] in skindata
volume = new WSlider("volume");
volume.addSliderListener(this);
skin.registerWidget(volume);
// popup menu sample
String [] menus = 
	{
	// menu #1
	"Menu 1" + WMenu.SUBMENU_DIVIDER + "Submenu-11" + WMenu.SUBMENU_DIVIDER + 
	            "Submenu-12",
	// menu #2  
	"Menu2" + WMenu.SUBMENU_DIVIDER + "Submenu21" + WMenu.SUBMENU_DIVIDER + 
			WMenu.MENU_DIVIDER + WMenu.SUBMENU_DIVIDER + "SubM31" ,
	WMenu.MENU_DIVIDER,
	"Menu3",
	"Menu4"
	};
	
popup = new WMenu("popup", menus, skinHandle);
popup.addMenuListener(this);
skin.registerWidget(popup, menus);
...

拨号盘小部件(广播调谐器/播放器的典型组件)应该具有一个类似如下的声明:

WDial position = new WDial("position")

这里, position 是皮肤数据文件( skindata )中定义的一个键,该文件包含诸如几何形状、图像等属性。之后, position.addDialListener(this) 调用将设置主类来监听由这个小部件激发的事件。 最后, skin.registerWidget(position) 将从 C 层级联调用 Java 层,然后返回。相同的方法序列也适用于所有 JSLIK 小部件。





回页首


探索源代码

本文已经提供了关于 SLIK 和 Java 平台在实践中如何协同工作的介绍。为了真正理解它究竟是如何工作的,您需要运行并研究源代码。随本文分发的代码包包含以下文件夹结构:

Slik_jni 文件夹结构

文件夹名称 文件类型
classes 已编译的 Java 类
myskins 示例皮肤
native Java JNI SLIK 库 Win32/Linux
src Java/C 源代码

还有两个提供用于测试目的的 shell 脚本:rundemo.bat 和 rundemo.sh。双击针对您的平台的相应脚本将会运行演示程序,并加载默认的皮肤,如图 5 所示。


图 5. PDA 演示皮肤
演示皮肤 PDA

按 Play 按钮将打开皮肤编辑器,您可以使用它来浏览或修改构成此皮肤的不同小部件。按 Back 按钮将启动打开文件对话框,您可以使用它来加载所提供的许多示例皮肤。

可以容易地使用自己最喜欢的 IDE 来编译该 Java 源代码。代码包中还提供了二进制版本,不过如果想要自己编译 C 代码,相应的编译说明已包括在这个项目的 README 文件中。本文中的代码已在以下平台上测试过:

  • Microsoft Windows 2000 和 XP。
  • Red Hat Linux 8.x 和 9,以及 SuSE Linux Workstation 8.x,两者都是基于 x86 的体系结构。




回页首


结束语

要成为桌面市场的重要竞争者,Java 开发人员被迫依赖 Java 本机接口(Java Native Interface,JNI)来提供特定平台的用户所预期的外观和感觉以及性能水平。

随着 Linux 在桌面操作系统市场的增长,高级用户界面将成为开发人员追求的主要目标。如果 Linux 要与 Windows 竞争,它需要具有易用性和出色的图形和视觉效果。GIMP 工具包为这种转变奠定了基石。

如今的 API,比如 WinAmp 和 Xine,都是平台相关的。SLIK 走出了实现统一的第一步。而且由于 JNI 可用于访问 SLIK API,因此 SLIK 代表了可同时供两个阵营使用的潜在工具。Java 开发人员可以编写具有本机 GUI 性能水平的桌面应用程序,而 Linux 开发人员能够提供可容易地移植到 Windows 的一流 GUI。

本文展示的 JNI API 将帮助您编写无需更改即可同时运行在 Linux 和 Windows 之下的带皮肤的 GUI。当然,您可以用 C 编写代码——如果那是您的首选语言的话;但是本文展示的代码将会给受到传统用户界面所包围 Java 语言迷带来新的机会。



参考资料

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文.

  • 下载与本文配套的 源代码



  • 要获得关于 SLIK 的介绍,请阅读 Vladimir Silva 撰写的“ GNOME 高级 UI 设计”一文( developerWorks,2004 年 1 月)。



  • 要使用 SLIK API,您需要 GIMP Toolkit运行库和开发环境。



  • 请访问 SLIK 项目的主页。SLIK 是在 Gnu 公共许可证(Gnu Public License,GPL)之下分发的。



  • 如果不熟悉 JNI,或者只是想提高一下,Scott Stricker 撰写的这篇 developerWorks 教程将帮助您起步:“ 用 JNI 进行Java编程”( developerWorks,2002 年 3 月)。



  • 了解关于 Java 和 本机类型之间的映射的更多信息,请参阅来自 Sun 的“Java 教程”的 相关部分



  • 了解关于 JNI 的功能的深入解释,请参阅也是来自 Sun 的“ Java Native Interface Trail”。



  • 还有其他哪些关于基于非矩形窗口的 GUI 的参考资料呢?请访问 IBM 的 Ease of Use 页面列出的文章以便对未来发展方向有更多的了解。



  • IBM 研究所正在研制能够识别和响应用户情感的计算机;当然,为了实现这点,他们需要音频-视觉语音处理作为人机界面。要了解关于此主题的更多信息,请阅读 R.W. Picard 撰写的文章“ Toward computers that recognize and respond to user emotion”( IBM Systems Journal,2000 年)。



  • 请访问 Developer Bookstore,获得技术书籍的详细列表,其中包括数百本 Java 相关的图书



  • developerWorksJava 技术专区 可以找到关于 Java 编程各个方面的数百篇文章。




关于作者

Vladimir Silva 出生在厄瓜多尔的基多。他于 1994 年从 Polytechnic Institute of the Army 获得系统分析员学位。同年,他作为交换学生来到美国,在中田纳西州立大学(Middle Tennessee State University)学习计算机科学。毕业后,他加入了 IBM“Web-Ahead”技术智囊团。他的兴趣包括网格计算、神经网络和人工智能。他还拥有包括 OCP、MCSD 和 MCP 在内的许多 IT 证书。可以通过 vsilva@us.ibm.comvladimir_silva@yahoo.com和 Vladimir 联系。




对本文的评价










回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款