`

JDK1.3 中的本地绘制支持

    博客分类:
  • JAVA
 
阅读更多

转帖:http://www.sudu.cn/info/index.php?op=article&id=316480

在 JDK1.3 出现以前,您仅能将 Java 本地接口用于非用户界面的工作。JDK 1.3 引入了新的 Java 2 AWT 本地接口,这使您可以在 Java 程序中使用非 Java 的 GUI 组件,尽管这样做会失去纯 Java 解决方案的可移植性。在使用 J2AWT 时,您必须针对要使用它的每个平台制作本地动态连接库或共享库。

下面这段话摘自 JDK1.3 的某个头文件,它说明了这种新的 API 的开发背景及原因:

 

AWT 支持使用本地 C 或 C++ 应用程序访问 AWT 的本地结构。这是为了便于将原有的 C 或 C++ 应用程序移植到 Java 并满足需要 ... [这些应用程序] 出于性能方面的原因在画布上自行进行本地绘制

在 JDK1.3 以前,Java 编程没有明确的方法来访问基层的同等 GUI 组件的句柄。在 JDK 1.3 中, Sun 公司创建了一种标准机制,通过这种机制,开发人员可以使本地 GUI 应用程序和库在 Java AWT Canvas 对象中进行绘制。这意味着现在有一种正式的、有保证的方法来获得支持这一功能的信息。当 JDK 1.3 与其他操作平台对接时,所有的接口都提供相同的信息 -- 而不管使用的是什么系统。JDK 1.3 的 Windows 版本和 Solaris 版本是首先提供这种支持的实现。

Sun 公司引入这一功能组件有几方面的原因。首先, JDK 1.3 使得人们可以将依赖第三方产品的复杂原有软件移植到 Java 上,而不必等到第三方产品本身完成移植以后。第二个原因即性能;如果本地的 GUI 代码经过人们长时期的努力得到优化,则原样保留这些软件具有重要的商业价值。

在本文中,我将介绍一些该功能部件的基本概念。我将逐步开发一个窗口小部件样例,该窗口小部件使用Win32 API 进行绘制。下图是最终的窗口小部件的快照,一个带有笑脸的圆形窗口。

分步概览
第一步,定义一个 Java 类 -- 比如说,Mywindow -- 使其继承 Canvas 类并重载 paint 方法。您使用 paint 方法执行 AWT 对象的绘制操作,并在覆盖该方法时加上 native 关键字。覆盖方法使您能够使用自己的本地代码。您必须构建自己的本地代码并把它编译成一个动态连接库,就像我们处理其他的 Java 本地接口应用程序一样,在本例中,我们将调用 MyWindow.DLL 库。在 Solaris 和 Linux 上则为共享对象或共享库。您还需要用 System.loadLibrary("MyWindow") 调用将 MyWindow.DLL 库加载到您的名为 MyWindow 的 Java 类中。

完成这一示例需要二个部分:其一是 MyWindow.Java ,它提供 Canvas 类的子类,其二是 MyWindow.CPP ,它包含基于 Java 本地接口的绘制子程序的入口点。 在参考资源部分可找到 MyWindow.JavaMyWindow.CPP 及自动执行编译的批处理文件 BUILD.BAT

第一步: 创建 MyWindow Java 类
J2AWT 用于这种方法时有一个主要的局限性:本地代码只能对 java.awt.Canvas 类的子类进行操作。这正是 MyWindow 继承 Canvas 类的原因。在 Java 应用程序中,您可以像使用 Canvas 的其它子类那样使用 MyWindow;在本例中,我将 MyWindow 添加到 Jwindow 中。

import java.awt.*;
import javax.swing.*;

public class MyWindow extends Canvas {
static {
//加载包含 paint 代码的库。
System.loadLibrary("MyWindow");
}

//绘制操作的本地入口点
public native void paint(Graphics g);

public static void main( String[] argv ){
Frame f = new Frame();
f.setSize(300,400);

JWindow w = new JWindow(f);
w.setBackground(new Color(0,0,0,255));
w.getContentPane().setBackground(new Color(0,0,0,255));
w.getContentPane().add(new MyWindow());
w.setBounds(300,300,300,300);
w.setVisible(true);
}
}

请注意:您是在静态块中加载 MyWindow.DLL。这正是 Java 应用程序访问本地代码的方式。(我稍候就会开发这段本地代码。)同时还应注意:paint 方法是用 native 关键字声明的,并且没有提供任何实现;这样做是为了让虚拟机知道,应该从在静态块中加载的动态连接库中调用该本地方法。

第二步:生成该类的 JNI 头文件
要为以上定义的类生成 Java 本地接口头文件,需使用 javah MyWindow.class 命令。首先应确保这个类文件在您的 CLASSPATH 中。以下是所生成的 MyWindow.h 的一部分,给出了函数声明。

/*
* Class: MyWindow
* Method: paint
* Signature: (Ljava/awt/Graphics;)V
*/
JNIEXPORT void JNICALL Java_MyWindow_paint
(JNIEnv *, jobject, jobject);

第三步:开发完整的 MyWindow.CPP
以下是完整的 MyWindow.CPP,其中包含 MyWindow.Java 中所需要的绘图程序的本地代码。

#include <windows.h>
#include <assert.h>
#include "jawt_md.h"
#include "MyWindow.h"

#define X(x) (int)(xLeft + (x)*xScale/100) // 缩放宏
#define Y(y) (int)(yTop + (y)*yScale/100) // 以使尺度在 0-100 之间
#define CX(x) (int)((x)*xScale/100)
#define CY(y) (int)((y)*yScale/100)

void DrawSmiley(HWND hWnd, HDC hdc);
HRGN hrgn = NULL;

JNIEXPORT void JNICALL
Java_MyWindow_paint(JNIEnv* env, jobject canvas, jobject graphics)
{
JAWT awt;
JAWT_DrawingSurface* ds;
JAWT_DrawingSurfaceInfo* dsi;
JAWT_Win32DrawingSurfaceInfo* dsi_win;
jboolean result;
jint lock;

// 获取 AWT
awt.version = JAWT_VERSION_1_3;
result = JAWT_GetAWT(env, &awt);
assert(result != JNI_FALSE);
// 获取绘图界面
ds = awt.GetDrawingSurface(env, canvas);
if(ds == NULL)
return;
// 锁定绘图表面
lock = ds->Lock(ds);
assert((lock & JAWT_LOCK_ERROR) == 0);

// 获取绘图表面的信息
dsi = ds->GetDrawingSurfaceInfo(ds);

// 获取特定平台的绘图信息
dsi_win = (JAWT_Win32DrawingSurfaceInfo*)dsi->platformInfo;

HDC hdc = dsi_win->hdc;
HWND hWnd = dsi_win->hwnd;
//////////////////////////////
// !!! 在此处进行绘图 !!! //
//////////////////////////////
if(hrgn == NULL)
{
RECT rcBounds;
GetWindowRect(hWnd,&rcBounds);
long xLeft = 0; // 用于缩放宏
long yTop = 0;
long xScale = rcBounds.right-rcBounds.left;
long yScale = rcBounds.bottom-rcBounds.top;
hrgn = CreateEllipticRgn(X(10), Y(15), X(Array0), Y(Array5));
SetWindowRgn(GetParent(hWnd),hrgn,TRUE);
InvalidateRect(hWnd,NULL,TRUE);
} else {
DrawSmiley(hWnd,hdc);
}
// 释放绘图表面的信息
ds->FreeDrawingSurfaceInfo(dsi);
// 为绘图表面解锁
ds->Unlock(ds);
// 释放绘图表面
awt.FreeDrawingSurface(ds);
}

void DrawSmiley(HWND hWnd, HDC hdc)
{
RECT rcBounds;
GetWindowRect(hWnd,&rcBounds);
long xLeft = 0; // 用于缩放宏
long yTop = 0;
long xScale = rcBounds.right-rcBounds.left;
long yScale = rcBounds.bottom-rcBounds.top;

// 基于控制大小的画笔宽度
int iPenWidth = max(CX(5), CY(5));
HBRUSH brushBlack;
HBRUSH brushYellow;
HPEN penBlack = CreatePen(PS_SOLID, iPenWidth, RGB(0x00,0x00,0x00));
// 用于绘制填充椭圆的空画笔
HPEN penNull = CreatePen(PS_NULL, 0, (COLORREF)0);

brushBlack = CreateSolidBrush(RGB(0x00,0x00,0x00));
brushYellow = CreateSolidBrush(RGB(0xff,0xff,0x00));

HPEN pPenSave = (HPEN)SelectObject(hdc, penBlack);
HBRUSH pBrushSave = (HBRUSH)SelectObject(hdc,brushYellow);
Ellipse(hdc,X(10), Y(15), X(Array0), Y(Array5)); // 头部

Arc(hdc,X(25), Y(10), X(75), Y(80), // 嘴部(微笑)
X(35), Y(70), X(65), Y(70));

SelectObject(hdc,&penNull); // 无绘图宽度
SelectObject(hdc,&brushBlack);

Ellipse(hdc,X(57), Y(35), X(65), Y(50));
Ellipse(hdc,X(35), Y(35), X(43), Y(50)); // 右眼
Ellipse(hdc,X(46), Y(50), X(54), Y(65)); // 鼻子

SetBkMode(hdc,TRANSPARENT); // 使用前景颜色

SelectObject(hdc,pBrushSave);
SelectObject(hdc,pPenSave);
}

这里的关键数据结构是 JAWT,它是在 jawt.h 中定义的(通过 jawt_md.h 包含在内)。它使程序可以访问本地代码在基于 Java 的 GUI 组件上绘图所需的所有信息。本地方法的第一部分是套式:置入 JAWT 结构,获得一个 JAWT_Win32DrawingSurfaceInfo 结构,锁定表面(请一次只使用一种绘图工具!),然后,获取一个 JAWT_DrawingSurfaceInfo 结构,该结构包含特定平台下绘图所必需的指针(在 platformInfo字段中)。它也包含绘图界面的矩形界限框及当前剪切区域。有关详细信息,请查看 jawt.hjawt_md.h (请参阅下面标题为 “构建环境”的部分)。

Java_MyWindow_paint 是一个入口点,JVM 通过调用它来绘制 MyWindow。辅助函数 DrawSmiley 使用 Win32 调用来完成实际的绘制工作。要在您的应用程序中包含 GetDrawingSurfaceInfo,请使用外部库 jawt.lib(请参阅 “构建环境”)。

第四步:编辑 BUILD.BAT
在运行 BUILD.BAT 之前首先对它进行编辑,并像如下所示的那样,为您的 Visual C++ 及 JDK1.3 设置路径。BUILD.BATMyWindow.java 进行编译,生成 MyWindow.h,然后将 MyWindow.CPP 编译为 MyWindow.DLL

SET DEVSTUDIO=D:Program FilesMicrosoft Visual StudioVCArray8
SET JDK13=D:JDK1.3

好了,一切准备就绪。在运行该样例之前,请确保 MyWindow.DLLJDK1.3BINJDK1.3JREBIN 都在 PATH 内,还要保证当前目录在 CLASSPATH 中;这将确保 MyWindow.class 会被成功加载。在确信 PATHCLASSPATH 都设置妥当后,在命令行输入 java MyWindow 来运行此应用程序。为方便您的使用,window.zip 中包含了一个批处理文件 RUN.BAT(请参阅参考资源)。要为 JDK 1.3 设置PATHCLASSPATH,请编辑 RUN.BAT

构建环境

  • 头文件:在 JDK 的 include 目录中新增了专用于 Windows 的 C 头文件。它们是:

    include/jawt.h.
    include/win32/jawt_md.h.

    依据 JavaSoft 网站的说明,这些头文件并不是 Java 2 平台正式规范的组成部分;提供这些头文件只是为希望用一种标准化方法访问本地绘图功能的开发人员提供一种便利。我认为这表示将 JDK 移植到其它平台的厂商可以不提供这个 API。

     

  • 库:一个以 jawt.lib 命名的新库已添加到 SDK 的库目录中。如前所述,这个库包含一个用于把 J2AWT 包含到您的应用程序中所需要的入口点。例如,要链接到 GetDrawingSurfaceInfo 入口点,您需要在您的程序中包含 jawt.lib

     

  • 工具:javah 工具用来为 Java 类的本地函数生成 C/C++ 头文件,javac 工具用来编译 Java 源文件。

     

小结
将原有软件系统移植到 Java 中并不容易,尤其是当原有软件包含高性能的绘图器时。Java 2 AWT 本地接口使得分阶段移植变得较为容易,它允许您首先移植对性能要求不高的代码,然后再移植关键的绘制代码。它同时使第三方窗口小部件开发厂商更能严肃地看待针对 Java 产品的开发。有了 Java 2 AWT 本地应用程序接口,您就可以移植原有的 GUI 代码,并更快地完成开发,这样就不会牺牲您为提高本地代码关键部分的性能而作的投资。

分享到:
评论

相关推荐

    java打印程序设计.pdf

    随后,在JDK 1.4中,官方推出了一整套名为“Java打印服务API”(Java Print Service API)的强大工具集,极大地提升了Java程序的打印能力。 本篇文章将详细介绍如何设计Java打印程序,并通过一个具体的例子来演示如何...

    将OpenGL嵌入Java AWT Canvas中

    JDK1.3引入了JAWT,它是Java平台的一个扩展,允许Java代码与本地窗口系统进行低级别的交互。通过JAWT,开发者可以访问本地窗口系统的特性,比如OpenGL,即使这些特性不直接包含在Java标准版(JavaSE)中。JAWT提供了...

    Java性能优化技巧集锦

    1.3 慎用异常 异常处理不应作为常规控制流程,因为创建异常对象和填充堆栈跟踪信息会消耗性能。只有在处理错误时才应使用异常。 1.4 不要重复初始化变量 Java会自动初始化变量,重复初始化只会增加不必要的计算。在...

    MyEclipse 6 Java EE 开发中文手册

    - **添加服务器**:将本地或远程的应用服务器添加到MyEclipse中。 - **配置服务器环境**:设置服务器的运行时环境和部署配置。 **6.2 部署应用** - **创建部署描述符**:为应用创建部署描述文件。 - **部署应用**...

    ETL工具kettle用户手册

    Kettle 支持将转换和作业保存在资源库中,以便于管理和共享。资源库可以是本地文件系统上的 XML 文件,也可以是远程的 Oracle、MySQL 等数据库。 **1.5 资源库自动登录** 为了方便使用,可以设置资源库的自动登录...

    ActionScript开发技术大全

    1.3小结 8 第2章搭建ActionScript3.0开发环境 9 2.1搭建基于FlashCS3IDE的开发环境 9 2.1.1安装FlashCS3ID 9 2.1.2安装FlashCS3IDEupdate9.0.2 11 2.1.3在FlashCS3IDE下创建ActionScript3.0项目 11 2.2搭建基于Flex...

    达内2011年在线测评系统5天全部源码ELTS_day01-day05

    a 采用了本地的C代码完成控件绘制, 性能很好, Eclipse b 实际应用不多 Swing 应用 1 JFrame 代表一个窗口控件 包括标题栏, 控制按钮, 和边框, 中间是内容面板 2 JLable 用来在GUI上显示Text 或者 图片的 3 ...

    《Android应用开发揭秘》附带光盘代码.

     1.3 小结  第2章 Android开发环境搭建  2.1 Android开发准备工作  2.2 开发包及其工具的安装和  配置  2.2.1 安装JDK和配置Java开发  环境  2.2.2 Eclipse的安装与汉化  2.2.3 SDK和ADT的安装和  配置  ...

    《Android应用开发揭秘》源码

     2.2.1 安装JDK和配置Java开发环境  2.2.2 Eclipse的安装与汉化  2.2.3 SDK和ADT的安装和配置  2.3 创建第一个Android项目——HeUoAndroid  2.3.1 创建HelloAndroid项目  2.3.2 运行HelloAndroid及模拟器的...

    Android应用开发揭秘

    2.2.1 安装JDK和配置Java开发环境 2.2.2 Eclipse的安装与汉化 2.2.3 SDK和ADT的安装和配置 2.3 创建第一个Android项目——HeUoAndroid 2.3.1 创建HelloAndroid项目 2.3.2 运行HelloAndroid及模拟器的使用 2.3.3 调试...

    Android应用开发揭秘pdf高清版

    2.2.1 安装JDK和配置Java开发 环境 2.2.2 Eclipse的安装与汉化 2.2.3 SDK和ADT的安装和 配置 2.3 创建第一个Android项目——HeUoAndroid 2.3.1 创建HelloAndroid项目 2.3.2 运行HelloAndroid及模拟器的使用 2.3.3 ...

    Java开发实战1200例(第1卷).(清华出版.李钟尉.陈丹丹).part3

    1.3 界面设计器 22 实例015 安装界面设计器 22 实例016 设计Windows系统的运行对话框 界面 23 实例017 设计计算器程序界面 26 实例018 设计关于进销存管理系统的界面 27 第2章 Java基础应用 29 2.1 基本语法 30 实例...

    精通ANDROID 3(中文版)1/2

    1.3 Dalvik VM剖析  1.4 理解Android软件栈  1.5 使用Android SDK开发最终用户应用程序  1.5.1 Android模拟器  1.5.2 Android UI  1.5.3 Android基础组件  1.5.4 高级UI概念  1.5.5 Android Service...

Global site tag (gtag.js) - Google Analytics