www6.software.ibm.com/developerworks/cn/education/java/j-java2d/tutorial/j-java2d-2-3.html
Java 2D 功能概述 |
<nobr>第 3 页(共3 页)</nobr> |
Java 2D 提供了实现非常复杂图形的机制,这些机制同 Java 平台的 GUI 体系结构很好地集成在一起。尤其是,Java 2D 为开发人员提供了下列功能:
Stroke
代理和一个 Paint
代理,前者定义用来绘制形状轮廓的笔,后者允许用纯色、渐变色和图案来填充形状。String
中的字符应用属性和样式信息来创建格式化文本。
图形环境 |
<nobr>第 1 页(共7 页)</nobr> |
图形环境的概念同在 GUI 平台上开发应用程序紧密相关。虽然通常将窗口和组件本身作为对象来表达,但仍然需要另一个接口来执行实际的绘制、着色以及文本输出操作。Java 语言中提供这些功能的基类称作 java.awt.Graphics
。从 java.awt.Component
类(所有窗口对象的基类)继承的类提供了一个名为 paint()
的方法,在需要重新绘制组件时,调用该方法。请参阅文档链接以获取至这些类的 java 文档的链接。
paint()
方法只有一个参数,该参数是 Graphics
类的实例。如果在某个继承了 Component
的类中覆盖了该方法,那么就可以使用该方法来控制在控制区域着何种颜色。例如,下面的类创建了一个带有蓝背景的面板:
Graphics 类特性 | <nobr>第 2 页(共7 页)</nobr> |
Graphics
类支持几种确定图形环境状态的特性。以下列出了部分特性:
Color
:当前绘制颜色,它属于 java.awt.Color
类型。所有的绘制、着色和纯文本输出都将以指定的颜色显示。Font
:当前字体,它属于 java.awt.Font
类型。它是将用于所有纯文本输出的字体。Clip
:java.awt.Shape
类型的对象,它充当用来定义几何形状的接口。该特性包含的形状定义了图形环境的区域,绘制将作用于该区域。通常情况下,这一形状与整个图形环境相同,但也并不一定如此。Shape
接口将在有关形状的一章中详细讨论。ClipBounds
:java.awt.Rectangle
对象,它表示将包围由 Clip
特性定义的 Shape
的最小矩形。它是只读特性。FontMetrics
:java.awt.FontMetrics
类型的只读特性。该对象含有关于图形环境中当前起作用的 Font
的信息。如同我们将看到的那样,获取此信息的这种机制已被 LineMetrics
类所取代(请参阅 LineMetrics 类)。Paint Mode
:该特性控制环境使用当前颜色的方式。如果调用了 setPaintMode()
方法,那么所有绘制操作都将使用当前颜色。如果调用了 setXORMode()
方法(该方法获取一个 Color
类型的参数),那么就用指定的颜色对像素做“XOR”操作。XOR 具有在重新绘制时恢复初始位模式的特性,因此它被用作橡皮擦除和动画操作。
Graphics 类方法 | <nobr>第 3 页(共7 页)</nobr> |
可以将 java.awt.Graphics
支持的非特性方法划分为三个常规类别之下:
draw3DRect() | drawArc() | drawBytes() | drawChars() |
drawImage() | drawLine() | drawOval() | drawPolygon() |
drawPolyline() | drawRect() | drawRoundRect() | drawString() |
fill3DRect() | fillArc() | fillOval() |
fillPolygon() | fillRect() | fillRoundRect() |
translate()
之类的杂项方法,它们将图形环境的起点从其缺省值 (0,0) 变成其它值。 请注意,没有对任意形状进行操作的操作。直到 Java 2D 出现以前,图形操作一直都是很有局限性的。还需注意的是,对于渲染具有属性的文本也没有直接支持;显示格式化文本是一项费事的任务,需要手工完成。
继承 AWT Graphics 类 | <nobr>第 4 页(共7 页)</nobr> |
为了实现 Java 2D 的新能力,创建了一个新类来继承 java.awt.Graphics
类。java.awt.Graphics2D
类(请参阅文档链接)除提供基类的所有功能之外,还提供了 Java 2D 的所有新功能。
要利用这些功能,您需要得到 Graphics2D
的实例而不是 Graphics
的实例。Component
类,包括那些定义为 Swing 库的一部分的类,都是围绕 Graphics
的使用实现的。由于 Graphics2D
是抽象类,因此我们不能创建其实例;那么我们该怎么办呢?
让那些敢于使用 Java 2D 的人感到非常高兴的是,在 JDK 版本 1.2 发布之后,Graphics
的所有实例实际上都是 Graphics2D
的实例。因为祖先类是 Graphics
,所以所有现有的组件代码仍然可以使用。因此,要访问 Graphics2D
的功能,类型强制转换操作是必需的:
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
}
虽然这样做看起来是一种危险的编程方式,但这种做法的确很常见。JVM 实现保证 Graphics
的任何实例都是 Graphics2D
的实例。
Component
对象和 java.awt.Image
对象都可以使用 Graphics2D
对象来进行操作。可以使用 javax.swing.ImageIcon
类或 Java 高级图像 API 来将外部图像文件装入 Image
的实例。可以在内存中“绘制”图像,可以通过获取表示内存中(in-memory)图像的 Graphics2D
对象来修改图像。
. . .
Image image = panel.createImage(100, 100);
Graphics2D g = (Graphics2D) image.getGraphics();
. . .
Graphics2D 类特性 | <nobr>第 5 页(共7 页)</nobr> |
以下是 Graphics2D
类提供的一些特性:
Background
:允许指定一个 Color
对象作为缺省颜色,当图形环境的部分被擦除时,会显示该缺省颜色。RenderingHints
:控制图形渲染的质量。我们将在渲染提示中详细讨论这方面的内容。Paint
:可以使用 Paint
接口来以任何纯色、渐变色或图案而不是简单的颜色来填充形状。Java 2D 修改了 Color
类以实现 Paint
接口。Stroke
:Java 2D 支持 Stroke
接口,该接口描述了一个虚拟笔,用于以各种宽度、颜色和图案来绘制线和曲线。Transform
:支持使用变换来修改绘制操作的工作方式。Graphics2D
的 Transform
特性属于 java.awt.geom.AffineTransform
类型,它代表一种用 3 乘 3 矩阵表示的数学规则。当操作由 Graphics2D
对象渲染时,无一例外地会对它们应用这一变换。Composite
:控制在绘制操作覆盖已经着色的像素时所发生的行为。该特性的值属于 java.awt.Composite
类型,此类型是一个描述颜色应该如何组合的接口。 Graphics2D 类方法 | <nobr>第 6 页(共7 页)</nobr> |
Graphics2D
提供许多附加方法,这些方法同 Java 2D 的其它能力交互:
clip()
、draw()
和 fill()
:其中的每种方法都获取一个 java.awt.Shape
类型的参数。这些方法允许擦除或绘制预先定义的或任意的形状,或者对它们进行着色。rotate()
、scale()
、translate()
和shear()
:通过添加另一种变换操作来修改环境的当前 Transform
。drawGlyphVector()
:渲染“字形”,“字形”是一种 Shape
,它代表某种 Font
的单字符。drawRenderableImage()
和 drawRenderableImage()
:提供一种能力,使得能够使用 Java 高级图像 API 来渲染实现 java.awt.image.renderable.RenderableImage
或 java.awt.image.RenderedImage 接口的图像。hit()
:检查指定形状是否同图形设备空间内的矩形相交了。该方法用于绘图程序以及游戏的碰撞测试。 渲染提示 | <nobr>第 7 页(共7 页)</nobr> |
Java 2D 图形环境的一项重要特性是 RenderingHints
特性。该特性由一个散列集支持,该散列集含有一些值,这些值允许控制渲染速度和质量。有些高级操作,例如抗锯齿和插值,可能是非常处理器和内存密集型的。提高图形质量会牺牲一些性能,RenderingHints
提供了一种方式对性能牺牲程度进行常规控制。
java.awt.RenderingHints
类含有一组散列键和值,可以将它们插入 Graphics2D
所包含的映射。键和值作为 RenderingHints
类的 public static
成员实现。Graphics2D
对象初始化时,将每个键都设置成了缺省值。修改某个值将改变 Graphics2D
对象的渲染方式,直至该值被复位。
支持下列键:
Graphics2D
允许控制是对所有元素还是仅仅对文本字符进行抗锯齿。
Shape 接口 | <nobr>第 1 页(共4 页)</nobr> |
java.awt.Shape
接口是一个代理,它描述几何形状的轮廓和特征。请参阅文档链接以获取至 Shape
接口、其它所有接口以及本章中所提及对象的 java 文档的链接。
实现该接口的对象必须提供 java.awt.geom.PathIterator
对象,该对象允许渲染程序一次一段地检索形状的路径。路径的每一段表示一条由渲染程序执行的绘制指令。可以将任何实现该接口的对象用作 Graphics2D
的 draw()
、fill()
或 clip()
方法的参数。
所有 Shape
对象还必须为渲染程序提供有关如何构造几何形状的其它信息。有几种版本的 contains()
方法,如果指定的坐标或形状在 Shape
对象的边界以内,那么这些方法会返回值 true
。intersects()
方法检查指定矩形中是否有任何区域与 Shape
重合。其它两种方法,getBounds()
和 getBounds2D()
,返回一个边界矩形,该矩形表示包围 Shape
形状的最小矩形。
Shape
接口的一项重要特性在于:它被设计成通过变换、添加或减去形状来构造新 Shape
对象。高级功能中涵盖了这些主题。
预定义的形状 | <nobr>第 2 页(共4 页)</nobr> |
Java 2D 提供了一些预定义类,它们实现了 Shape
接口。实现一个实现了 Shape
接口的定制类实际上是不常见的(也是困难的),因为所提供的那些类几乎可以创建任何可以想象的形状。
新的 Java 2D Shape
类都有“2D”后缀。这些新的形状使用浮点值(而不是整数)来描述其几何形状。而且,Java 2D 之前的 Shape
类,诸如 Rectangle
,现在继承了对应的 2D 类,这使得无须进行类型强制转换就能够更容易地转换到新的渲染引擎。
以下是 Java 2D API 中的新 Shape
类:
Point2D
、Dimension2D
和 Line2D
:这些类提供对应标准类的浮点版本。因为类成员不可访问,所以这些版本需要使用取值(读)方法来检索值。QuadCurve2D
和 CubicCurve2D
:这些类表示二次和三次“参变”曲线。将这些曲线实现成贝塞尔曲线,贝塞尔曲线由两个端点以及一个或两个控制点指定。贝塞尔曲线创建了适合于大多数表示的曲线。GeneralPath
:它是一个 Shape
对象,该对象提供将片段连接成复杂几何形状的命令。它允许使用直线和贝塞尔曲线序列来构建图形,还允许用子路径来组成路径的片段。 有关使用 GeneralPath
的示例,请参阅示例:模拟时钟。
Stroke 和 Paint 接口 | <nobr>第 3 页(共4 页)</nobr> |
这两个接口为 Java 2D 体系结构提供了一些十分强大的能力。Graphics2D
类描述了两种特性,Stroke
和 Paint
,它们含有一些代理对象,这些对象描述了用来绘制和填充形状的虚拟笔和刷子。
Stroke
接口由 java.awt.BasicStroke
类实现。该类允许进行大量的选择以修改线的绘制细节。您可以指定 BasicStroke
宽度,也可以指定对名为柱头和交点的路径上端点和交点的“装饰”。现在也可以绘制点划线了,只须设置 BasicStroke
的破折号属性即可。
Paint
接口有几个具体的实现,它们允许用纯色、渐变色或图案来填充形状。对 java.awt.Color
类做了一些调整以实现 Paint
,并且可以用于纯色填充。java.awt.GradientPaint
类允许用线性颜色渐变色来填充形状,线性颜色渐变色允许在两个指定的 Color
对象之间创建过渡。可以将渐变色设置成“周期性的”,这将导致渐变色图案重复出现。最后,还提供了 java.awt.TexturePaint
类,它用由 BufferedImage
描述的图案填充形状(请参阅缓冲的图像以获取更多信息)。
字体支持概述 | <nobr>第 1 页(共5 页)</nobr> |
带属性的字符串 | <nobr>第 2 页(共5 页)</nobr> |
虽然 Graphics2D
中仍然提供 drawString()
的旧版本(该版本接收一个 String
作为参数),但 Graphics2D
中已经有了更为强大的能力,允许渲染格式化字符串。
java.text.AttributedString
类允许构造含有属性信息的字符串。当使用 AttributedString
时,它总是由实现了 java.text.AttributedCharacterIterator
接口的对象来指定。其迭代器类似于 GeneralPath
的迭代器,因为它允许文本布局和渲染类将文本分割成一个个属性序列。
要创建 AttributedString
,只须使用 String
对象或现有的 AttributedString
。使用 addAttribute()
方法创建了 AttributedString
之后,就可以为其提供属性。该方法获取一个 AttributedCharacterIterator.Attribute
类型的参数,以及一个可选的属性序列的起始和终止索引。省略这些索引将导致将指定的属性应用于整个字符串。
可以用于 AttributedString
的部分属性有:
BACKGROUND
:控制背景颜色;可以设置为透明。FOREGROUND
:控制前景颜色;覆盖图形环境的 Paint
特性。FONT
:控制用于渲染字符的字体;FONT 覆盖图形环境的 Font
特性。FAMILY
、SIZE
和 WEIGHT
:提供另一种方法来指定字符字体,而不是直接创建 Font
对象。这些属性也代替了图形环境的 Font
特性。CHAR_REPLACEMENT
:允许用字符替换任意 Shape
对象。这是另外一种创建定制字体的方法。TRANSFORM
:对 AttributedString
中的全部或部分字符应用某个变换(参阅变换)。
TextLayout 类 | <nobr>第 3 页(共5 页)</nobr> |
java.awt.font.TextLayout
类提供带属性的字符的图形表示。TextLayout
用于从 AttributedString
派生诸如被渲染的文本将有多大之类的信息;它还包含一些方法,提供插入记号(表示文本中的插入点的闪烁形状)、突出显示和点击测试。注:TextLayout
同控制容器中组件布局的 java.awt.LayoutManager
类无关。
TextLayout
是一种不可变对象,这意味着一旦被构建,其任何特性都无法被更改。如果源 AttributedString
改变了,那么就必须创建一个新 TextLayout
实例。从处理器和内存开销的意义上说,它是一种成本相对较高的对象,因此使用它的程序必须尽可能少地创建 TextLayout
实例。
Graphics2D
并没有任何从 TextLayout
进行绘制的方法,但 TextLayout
却有它自己的 draw()
方法版本,它的这一方法获取一个 Graphics2D
作为其参数之一。
TextLayout
中最常用的信息元素是一些只读特性,它们返回有关显示渲染的字符串所需大小的信息。所有这些特性都是 float
类型:
getAdvance()
:渲染字符串的宽度。getAscent()
:渲染字符串从布局顶部到基线(一条想象中的、沿着字符底部的线)的高度。getDescent()
:渲染字符串从基线到布局底部的高度。它是“p”和“q”之类的小写字符从那条线下面扩展的区域。getLeading()
:建议的文本行之间的空间。这一术语来源于铅字时代,当时使用一条铅带将打印行分隔开来。
LineMetrics 类 | <nobr>第 4 页(共5 页)</nobr> |
同环境交互 | <nobr>第 1 页(共4 页)</nobr> |
Java 2D 的出现使得必须创建一些附加类,这些类将允许程序员以一种平台中立的方式访问特定于环境的信息。这一章对这些类做了概述。
GraphicsEnvironment 类 | <nobr>第 2 页(共4 页)</nobr> |
GraphicsDevice 类 | <nobr>第 3 页(共4 页)</nobr> |
一旦获得了 GraphicsEnvironment
,程序员就可以用它来查询可用的输出设备的数量和类型。调用 GraphicsEnvironment
实例上的 getDefaultScreenDevice()
方法会返回一个 GraphicsDevice
实例,这个实例通常表示主要的图形输出设备 ― 屏幕。如果工作站装备了多个物理屏幕,那么每个设备都可以被独立处理。GraphicsEnvironment
方法 getScreenDevices()
返回一组 GraphicsDevice
对象 ― 其中的每个对象表示一个连接到系统的屏幕。
GraphicsDevice
有两个信息特性。每个设备都有一个可由 getIDString()
方法访问的名称以及一个由 getType()
方法检索的类型。设备类型将总是下列值和相关意义之一:
GraphicsDevice.TYPE_RASTER_SCREEN
:设备是监视器。GraphicsDevice.TYPE_PRINTER
:设备是打印机。GraphicsDevice.TYPE_IMAGE_BUFFER
:允许在内存中进行绘制操作的“虚拟”设备。(用户不能查看该设备类型。) 大多数输出设备有多种不同显示方式。监视器和视频适配器可以用不同数目的可用颜色显示不同的屏幕分辨率。打印机也可以提供包括方向和打印质量在内的许多打印选项。图形设备可供使用的各种方式封装在另一个对象 GraphicsConfiguration
中。
GraphicsConfiguration 类 | <nobr>第 4 页(共4 页)</nobr> |
GraphicsConfiguration
类表示 GraphicsDevice
的一种操作方式。该类的实例依次从 GraphicsDevice
实例检索。可以通过调用 getDefaultConfiguration()
来检索任何设备的缺省配置,但可以使用 getConfigurations()
方法从 GraphicsDevice
检索一组 GraphicsConfiguration
对象。
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice defaultDevice = env.getDefaultScreenDevice();
GraphicsConfiguration[] configs = defaultDevice.getConfigurations();
for (int z=0; z < configs.length; z++)
System.out.println(configs[z]);
上述代码(在我的机器上)产生下列输出:
> Win32GraphicsConfig[dev=Win32GraphicsDevice[screen=0],pixfmt=1]
> Win32GraphicsConfig[dev=Win32GraphicsDevice[screen=0],pixfmt=2]
> Win32GraphicsConfig[dev=Win32GraphicsDevice[screen=0],pixfmt=5]
> Win32GraphicsConfig[dev=Win32GraphicsDevice[screen=0],pixfmt=6]
> Win32GraphicsConfig[dev=Win32GraphicsDevice[screen=0],pixfmt=9]
> Win32GraphicsConfig[dev=Win32GraphicsDevice[screen=0],pixfmt=10]
pixfmt
特性表示颜色深度;也就是,使用多少位来表示特定的颜色。使用的位越多,可被显示的颜色就越多。随着颜色深度增加,软件性能会相应地下降,因为表示每个像素所需的物理内存和虚拟内存的数量会更大。
GraphicsConfiguration
类有另外一种重要用途,这种用途同缓冲的图像的使用有关。特别地,如果用户想创建同特定 GraphicsConfiguration
兼容的 BufferedImage
,那么可以使用 createCompatibleImage()
方法。程序员提供所希望的缓冲区图像的高度、宽度和透明度。
构造型区域几何 | <nobr>第 1 页(共5 页)</nobr> |
从图像创建缓冲图像 | <nobr>第 3 页(共5 页)</nobr> |
除了内置的形状类的能力之外,Java 2D 还具有通过组合 Shape
对象来构造复杂形状的能力。这一功能称为构造型区域几何 或 CAG。
Shape
接口的特殊实现,即 java.awt.geom.Area
类,在 Shape
类上执行各种二进制操作。
有四种组合操作:
add()
:组合两个形状。它不必有任何共同区域。subtract()
:除去指定区域。intersect()
:创建一个只含有源区域和目标区域共有空间的新的形状。exclusiveOr()
:从两个形状中除去重叠区域。 上述操作创建一个新 Area
,它是源对象和指定的目标对象组合的结果。源对象可以在 Area
对象的构造器中指定。因为 Area
本身就是 Shape
,所以可以组合任意复杂的形状。
请记住,渲染引擎必须处理的段的数目随形状的复杂而上升,从而会降低性能。
缓冲的图像 | <nobr>第 2 页(共5 页)</nobr> |
类似于 Graphics2D
继承 Graphics
的方式,java.awt.image.BufferedImage
通过在 Java 2D API 中提供一组强大的功能替换了 AWT Image
类。BufferedImage
是 Image
的继承,因此它同 Java 2D 以前的代码完全兼容。JDK 1.2 的实现规定:图形系统返回的任何 Image
对象都是 BufferedImage
,这与所有的图形环境实际上都是 Graphics2D
实例很相象。
Image
只可通过获取图形环境然后在其上着色来进行修改,而 BufferedImage
与 Image
不同,它提供对底层图像数据的直接访问。BufferedImage
还提供通过称为过滤的过程来进行修改,这一过程在图像上提供了类似于高级图像编辑软件中找到的那些操作。
对 BufferedImage
进行了特殊设计以支持 Java 高级图像 API,它支持多色模式和光栅格式。
下面的代码获取一个 Image
类型的对象并将其转换为一个 BufferedImage
实例:
变换 | <nobr>第 4 页(共5 页)</nobr> |
变换是一组操作,它们基于对图形元素执行各种修改的矩阵算术。要使用这些能力并不一定要理解线性代数;java.awt.geom.AffineTransform
类封装了所有可用操作。可以将变换应用于 Shape
、Font
和 BufferedImage
,也可以将它直接应用于图形环境。
变换可以提供几种操作,这些操作可以结合起来使用。
正如您能想象到的那样,变换是不可交换的。也就是,如果以不同顺序应用一组变换操作,那么它们可能会产生不同的结果。很显然,如果您按照某人的吩咐“转九十度再向北走三步”与您“向北走三步再转九十度”所到达的地方将不一样。
开发 Java 2D 时,考虑了平台中立性,这意味着:当应用程序运行于不同硬件环境上时,必须有一些方法供图形程序来了解操作环境的能力。而且,还需要一种机制供 GUI 程序发现可以获得什么输出设备以供显示,以及确定系统上可以获得什么字体资源。GraphicsEnvironment
类为这些目的服务。
GraphicsEnvironment
是一个抽象类,可以在 java.awt
包中找到。它具有一个静态方法 getLocalGraphicsEnvironment()
,该方法返回适合于图形开发人员使用的 GraphicsEnvironment
实例。
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
可以使用这一引用,例如用来检索 JVM 可访问的可用字体库名称。它替代了 java.awt.Tookit
中受到批评的 getFontList()
方法:
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
String[] fontNames = env.getAvailableFontFamilyNames();
for (int k=0; k < fontNames.length; k++)
System.out.println(fontNames[k]);
还提供了该方法的另一种版本,该版本获取 Locale
对象作为参数。此版本允许用户检索特定于特定区域的字体。
GraphicsEnvironment
还有一种名为 getAllFonts()
的方法,它检索一个 java.awt.Font
对象数组,其中的每个对象对应于系统上安装的一种字体类型。建议不要使用该方法,因为那会导致付出高昂的性能代价,而这一代价会随目标平台上的字体的增加而增加。
AWT 类 FontMetrics
不足以表达 Java 2D 中更新的字体管理系统中的信息。为了升级 java.awt.Font
类中的 API,向其中添加了另一个名为 getLineMetrics()
的方法,该方法获取一个 String
或 AttributedString
并返回该特定字符集的字体尺寸。
与 TextLayout
不同,LineMetrics
不是一种图形表示,例如,它不提供渲染字符串的增加(宽度)。它返回字符串的上升、下降和行间距,它还含有一个方法 getHeight()
,该方法返回上升、下降和行间距的和。和所有 Java 2D 文本方法一样,大小都表示为 float
类型。
从系统开销的观点来看,LineMetrics
是一种比 TextLayout
量级轻得多的类。
Java 2D 提供复杂文本输出能力。Java 2D 和一个重新设计的字体引擎支持使用属性集对字符串的单个字符进行操作。诸如透明度、颜色插值和抗锯齿之类的其它 Java 2D 功能也能用于字体处理。
字体渲染是一种高度复杂的机制,因为字体是由许多复杂的形状组成的,而这些形状被应用了很多几何变换。为了将渲染能力同图形环境以及字体渲染引擎分隔开来,使用了一个名为 java.awt.font.FontRenderContext
的类。该类封装了要正确地确定文本的可查看大小所需的信息。Graphics2D
提供了一种方法来根据图形环境的当前属性获取 FontRenderContext
,也可以为离屏(off-screen)计算手工构造 FontRenderContext
。