跳转到主要内容

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

所有提交的信息确保安全。

  • 关闭 [x]

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

所有提交的信息确保安全。

  • 关闭 [x]

创建用于翻转效果的 Java2D 合成

在 Java 应用程序中如何定义用于翻转和选择事件的变量

Joe Winchester (winchest@uk.ibm.com), 高级软件工程师, EMC
Joe Winchester 是 WebSphere Application Developer(以前被称为 WebSphere Studio)的主要开发者。他在联合王国赫斯利(Hursley)实验室工作,是一名高级软件工程师。您可以通过 winchest@uk.ibm.com 与 Joe 联系。
Renee Schwartz (rsch@us.ibm.com), 软件可视化设计师, EMC
Renee Schwartz 作为一名可视化设计师在位于北卡罗莱纳州罗利(Raleigh)市的三角园研究实验室(Research Triangle Park Lab)为 IBM 工作。她的研究重点是 WebSphere 产品,把她传统的可视化设计技能和知识应用到 Web 和 Java 应用程序开发中。您可以通过 rsch@us.ibm.com与 Renee 联系。

简介: 在创建或使用 Java 应用程序的时候,您可能有过使用缺省的 Swing 机制获得一些有趣的翻转和/或选择效果的经历。Java2D API 允许您通过编写方法定义用于翻转和选择事件的合成值,通过使用 Java2D API 可以得到更一致、更普通的结果。在本文中,您可以发现在 Java 语言中如何构造图像以及使用 AWT composite 可以如何操作图像。

发布日期: 2002 年 11 月 20 日
级别: 初级
访问情况 : 1000 次浏览
评论: 


引言

许多应用程序都有包含图标的按钮,当按钮被选中或者鼠标移到按钮上时,按钮的图标会发生改变。Web 浏览器(如 Microsoft Internet Explorer)中的工具栏按钮就是这种现象的一个示例,其中的按钮图形是灰色的,当鼠标移动到按钮上时,图形会变成有颜色的。为了实现这种效果,要创建两组图形;一组用于正常状态,一组用于当鼠标移动到按钮上时的活动状态。然而,对于设计者来说,创建两组图形很费时间,并且意味着在需要更改图形时,必须对这两组图形做双倍的维护工作。更优化的办法是只使用一个图标,而图形效果则通过编程来实现,从而避免创建和维护一个单独图标的开销。本文描述的问题是针对如 图 1所示的向导页中的一组按钮的,当用户在每个按钮上悬停时,图形会发生改变以向用户表示该按钮是活动的。


图 1.Web Module 按钮在鼠标移动到它上面时处于活动状态并且图形已经改变。
Web Module 按钮在鼠标移动到它上面时处于活动状态并且图形已经改变。

在本文中,我们将说明如何通过使用 Java2D API 创建一个能够接受图像并创建所期望的效果的类来实现上述效果。这需要我们理解如何构造图像以及如何使用 AWT composite 来操作这些图像。


背景知识

javax.swing.JButton 类有一个布尔属性 rolloverEnabled 和一个 rolloverIcon 属性,后者的类型是 javax.swing.Icon。如果 rolloverEnabled 为 true,则当鼠标移动到按钮上时,rolloverIcon 的值就被用到该按钮的图形上。我们认定,一个优秀的解决方案应该有一个新的称为 RolloverImageIcon 的类,我们将在其构造函数中给它提供一张图像,然后,这个类会在绘制图标之前对图标进行操作。

这样,用于创建 rolloverButton 的代码将如下所示:

 JButton button = new JButton(regularIcon);
 button.setRolloverEnabled(true);
 button.setRolloverIcon(new RolloverIcon(regularIcon));

下一步是创建能够将初始图标包装起来并用 图 1所示的图形效果给图标着色的 RolloverIcon 类。


RolloverIcon

RolloverIcon 类将实现 javax.swing.Icon 接口,这个接口允许按钮的 rolloverIcon 属性取有效值并将初始图标存储到名为 fIcon 的实例变量中:

public class RolloverIcon implements Icon { protected Icon fIcon; {

一种变通的解决方案可能是创建 JButton 的子类,并将翻转效果封装到这个子类中,不过,通过将逻辑放到 RolloverIcon 类中,图形效果也可以在其它情形(如复选框或菜单项)中使用。假设想得到定制的子类,那么使用创建图形效果的逻辑并将这个逻辑委托给 RolloverIcon 的实例是个很简单的做法。

javax.swing.Icon 接口有三个方法: getIconWidth()、getIconHeight() 和 paintIcon(Component,Graphics,x,y) 。前两个方法可以委托给 RolloverIcon 正在为它创建图形效果的图标,因为翻转图像的大小将与原来图像的相同。

public int getIconHeight() {
  return fIcon.getIconHeight();
}
public int getIconWidth() {
  return fIcon.getIconWidth();
}

绘制(paint)方法是将对图形上下文进行实际绘图的方法。Component 参数代表正在为之绘制图标的控件,如 javax.swing.JButton 的实例。这个方法允许我们访问诸如控件的字体、插图等细节问题,以及我们在绘制图标时可能想考虑的其它属性。x 和 y 参数是在绘图表面上正在对该图形进行着色的位置。这些位置是绘图表面的绝对位置,与从组件的 getLocation() 返回的值不同,后者是按钮相对于其父容器的位置。绘图 API 需要使用绝对值,以便将它们传递到 paintIcon 方法中,避免不得不遍历组件的所有父容器来计算这些值。

graphics 参数是代表绘图表面的对象。虽然它的类型是 java.awt.Graphics,但它将是 java.awt.Graphics2D 的实例。Graphics2D 抽象类是 Java2D API 的一部分,它是出于向后兼容的目的作为 Java2 平台的一部分引入的,绘制方法的参数没有被重新转换为 Graphics2D 类型,尽管只要使用的 JRE 是 Java2 或更高版本就可以保证这个参数是 java.awt.Graphics2D 的实例。

paintIcon 方法的完全说明如下所示:

public void paintIcon(Component c, Graphics g, int x, int y);

RolloverIcon 实例将包装我们希望用 图 1中所示的图形效果绘制的初始图标。我们可以使用 Graphics2D 对象的 composite 属性做到这一点。composite 属性的类型是 java.awt.Composite 接口,并且由 Graphics2D 对象完成的对图形表面的所有图元绘图都直接通过对象的 composite 进行。存在许多现成的合成类,如其中有一个是用于创建异或(XOR)效果的。XORComposite 是 sun.java2d.loops.XORComposite 类,它的构造函数以要着色成异或效果的绘图的颜色为参数。每种颜色由红、绿和蓝值组成,如果用黑色(黑色的 r、g、b 值为 0、0、0)对颜色进行异或,则该颜色将被反转。要看到这种效果,绘制方法可以编写成如下所示:

public void paintIcon(Component c, Graphics g, int x, int y) {
  Graphics2D g2D = (Graphics2D)g;
  Composite oldComposite = g2D.getComposite();
  g2D.setComposite(new sun.java2d.loops.XORComposite(Color.black));
  fIcon.paintIcon(c,g,x,y);
  g2D.setComposite(oldComposite);
}

要对初始图标(保存在实例变量 fIcon 中)进行着色,我们只要遵从它的 paintIcon(Component,Graphics,int,int) 方法(这个方法已经将图形的 composite 预先设置为 XORComposite 对象)就行了。在操作图形对象的属性时,在完成之后将这些属性恢复成原来的值是一种很好的做法。在上面的方法中显示了这一点,我们在修改 composite 之前先将原来的 composite 存储起来,然后在完成修改之后将它恢复。如果您没有这样做,则 XORComposite 将会被保留在图形对象中,并且会影响到随后的所有绘图操作。

图 2显示了 XORComposite 的效果。上面那行按钮是 RolloverIcon 被设置成 rolloverIcon 属性的原来的按钮。下面那行按钮是被永久性地设置的,用来显示被设置成 icon 属性的 RolloverIcon 的结果。XORComposite 接受图标并通过用 0 对图标的每个像素进行异或来转换它。


图 2. XORComposite 可以用来控制如何对图标进行着色。
XORComposite 可以用来控制如何对图标进行着色。

异或的效果虽然不是如 图 1 所示的结果,但它确实表明了 composite 是如何负责在图标的绘图表面上进行着色的。如果我们创建自己的能让我们访问图标的精确着色的合成类,那我们就应该能够实现我们所期望的翻转效果。我们的类将叫做 RolloverComposite ,并且 paintIcon 方法可以在对初始图标进行着色之前将所期望的效果在图形对象中进行设置。

在实现 RolloverComposite 类之前,我们需要了解关于在 Java 语言中如何表示颜色以及如何将这些颜色绘制到绘图表面上去的更多知识。


RolloverComposite

RolloverComposite 必须实现 java.awt.Composite 接口。这个类有一个单独的 Entry Helpers 方法,该方法返回 java.awt.CompositeContext 的实例。RolloverComposite 不负责对图形表面上的像素的实际图元进行操作,而是把这项任务委托给能够在多线程环境中维护状态和工作的合成上下文对象,因为针对单个合成可以存在多个合成上下文对象。

public CompositeContext createContext(ColorModel srcColorModel, ColorModel 
dstColorModel, RenderingHints hints) ; 

ColorModel 抽象类具有能在颜色组件和基本的红、绿、蓝以及 alpha 组件之间进行转换的 API。 RenderingHints 是来自 Graphics2D 的着色提示,RolloverComposite 在需要时可以使用它,例如,在进行文本着色时是否要使用抗锯齿,或者应该使用什么类型的行合并样式。 CompositeContext 是实际操作原始像素的线程安全的对象,我们将创建一个实现这个目的的匿名内部类,如下所示:

return new CompositeContext(){
  public void dispose(){
  }
  public void compose(Raster src, Raster dstIn, WritableRaster dstOut){

CompositeContext 接口有两个方法,即 dispose()compose(Raster,Raster,WritableRaster) 。dispose 方法让我们清除任何已经分配的资源,而 compose 方法则负责图元绘图的方法。我们没有任何需要清除的对象,所以可以有一个 dispose() 的不进行任何操作的实现, compose(...) 方法则是我们对像素进行操作的地方。

compose(...) 方法的三个参数是,用于源图像的 Raster、用于目标图像的 Raster 以及表示图形表面上将被着色的输出的 WritableRaster 。要访问给定位置的原始像素,可以使用 getPixel(int x, int y, pixel int[]) 方法。int[] 参数是一个由四个参数组成的数组,表示像素的红、绿、蓝以及 alpha 值。src 参数包含源参数的光栅, dstOut 参数则是具有 setPixel(int x, int y, pixel int[]) 方法的 WritableRaster。通过反复操作来自源图像的每个像素,我们就可以创建具有完成操作后的值的新像素,并将它设置到 dstOut 中,这个 dstOut 将被绘制到图形表面上。compose 方法的代码如下所示:

            // Get the source pixels 
            int[] srcPixels = new int[4]; 
            src.getPixel(x,y,srcPixels); 
            // Ignore transparent pixels 
            if (srcPixels[3] != 0){ 
                // Lighten each color by 1/2, and increasing the blue 
                srcPixels[0] = srcPixels[0] / 2; 
                srcPixels[1] = srcPixels[1] / 2; 
                srcPixels[2] = srcPixels[2] / 2 + 68; 
                dstOut.setPixel(x,y,srcPixels); 
            } 
        } 
    }; 
} 

请注意,我们检查了第四个像素 (srcPixels[3]) ,看看它是不是透明的。如果它在源图像上时是透明的,则我们就不写任何内容到 dstOut 中,因为我们想让绘图表面现有的像素保持不变。


完成代码

创建了返回 CompositeContext (它负责把所期望的像素值设置到要被绘制到图形表面上的光栅中)的 RolloverComposite 类之后,我们需要在 RolloverIcon 类中完成代码。早些时候我们在测试异或合成时,我们要在通过 paintIcon(...) 方法将它设置到图形的 composite 中之前先创建一个新实例。在 Java 语言中,创建对象的开销很大,因为必须分配内存,然后在不再需要时对所得到的对象进行垃圾回收,所以,我们将改用可以根据需要进行重用的 RolloverComposite 类的单子(singleton)实例。我们不必担心多个图标或图形对象会具有对同一个对象的访问权,因为所有的状态都由 CompositeContext 保存,CompositeContext 可以根据需要重新创建并能够提供线程安全的访问。

RolloverComposite 将在叫做 DEFAULT 的公共静态(public static)域包含它的单个实例。为了迫使人们使用这个单子实例(singleton)而不是每次创建一个新对象,构造函数被声明成私有的(private),这样便只有 RolloverComposite 类能够创建它自己的实例。

public class RolloverComposite implements Composite { 
  public static RolloverComposite DEFAULT = new RolloverComposite(); 
  private RolloverComposite(){ } 

RolloverIconpaintIcon 方法在绘制它包装的初始图标之前需要将图形参数的 composite 设置成单子 RolloverComposite 。绘制完成后需要恢复原来的 composite,以确保随后的任何绘图操作不会受到由让定制的 composite 实例保留超过所需要的时间所带来的不必要影响。

public void paintIcon(Component c, Graphics g, int x, int y) {
  Graphics2D g2D = (Graphics2D)g;
  Composite oldComposite = g2D.getComposite(); 
  g2D.setComposite(RolloverComposite.DEFAULT); 
  fIcon.paintIcon(c,g,x,y); 
  g2D.setComposite(oldComposite); 
} 


完成的结果

编写完 RolloverIcon 类的代码后,现在我们需要测试它。为此,我们编写了一个测试工具,它创建两行按钮,每行有四个,其中上面那行具有翻转效果,下面那行按钮的图标被永久性地设置成翻转图标。您可以从下面的 参考资料部分下载这个测试工具的代码以及 RolloverIconRolloverComposite 类的所有代码。


图 3. 测试工具显示了 RolloverIcon 的效果。
测试工具显示了 RolloverIcon 的效果

上图显示了 RolloverIcon 完成后的结果。上面那行的原来的图形被调暗了 50% 并且蓝色的亮度被稍微调高了一点,以达到我们原来期望的图形效果。


结束语

RolloverIcon 是 Java2D API 的可扩展性有多么好的一个示例,这样我们便可以访问图形子系统中级别很低的组件,从而创建我们自己的扩展。对图形对象所使用的合成进行控制是我们用来很好地实现图形效果的办法,但对 Java2D 的工作机制有了更深的理解后,我们还可以实现许多其它的定制效果。关于完整的 API 的更多信息可以在众多讲述 Java2D 的优秀书籍中的一本中找到(请参阅 参考资料)。


参考资料

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

  • 请阅读 Java 2D API Graphics,Vincent J. Hardy 著,Prentice Hall PTR 出版,ISBN 0130142662。这是一本帮助您学习 Java 2D 的优秀书籍。这本书写得很好并且有许多不错的示例。


  • 请阅读 Pure Jfc 2d Graphics and Imaging,Satyaraj 和 Pantham 博士著,Sams 出版,ISBN 0672316692。这本书面向的是更有经验的开发者并带有用于实现某些高级图形效果的代码样本。


  • 请参阅这份优秀的 在线 Java2D API 教程,它说明了使用 Java2D API 可能实现的不同效果并包含有样本代码。


  • 请下载一些带有代码的 示例,这些示例说明了使用 Java2D API 可以实现的不同效果。这些样本可以在浏览器插件中运行。


  • 至于本文在前面描述过的测试部件,您可以 下载它们的代码

作者简介

Joe Winchester 是 WebSphere Application Developer(以前被称为 WebSphere Studio)的主要开发者。他在联合王国赫斯利(Hursley)实验室工作,是一名高级软件工程师。您可以通过 winchest@uk.ibm.com 与 Joe 联系。

Renee Schwartz 作为一名可视化设计师在位于北卡罗莱纳州罗利(Raleigh)市的三角园研究实验室(Research Triangle Park Lab)为 IBM 工作。她的研究重点是 WebSphere 产品,把她传统的可视化设计技能和知识应用到 Web 和 Java 应用程序开发中。您可以通过 rsch@us.ibm.com与 Renee 联系。

关于报告滥用的帮助

报告滥用

谢谢! 此内容已经标识给管理员注意。


关于报告滥用的帮助

报告滥用

报告滥用提交失败。 请稍后重试。


developerWorks:登录


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 使用条款

 


当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

请选择您的昵称:

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

(长度在 3 至 31 个字符之间)


单击提交则表示您同意developerWorks 的条款和条件。 使用条款.

 


为本文评分

评论

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology
ArticleID=52908
ArticleTitle=创建用于翻转效果的 Java2D 合成
publish-date=11202002
author1-email=winchest@uk.ibm.com
author1-email-cc=winchest@uk.ibm.com
author2-email=rsch@us.ibm.com
author2-email-cc=rsch@us.ibm.com

标签

Help
使用 搜索 文本框在 My developerWorks 中查找包含该标签的所有内容。

使用 滑动条 调节标签的数量。

热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。

我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。

使用搜索文本框在 My developerWorks 中查找包含该标签的所有内容。热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。