第十章 图形程序设计

Swing 概述

AWT是“ 一次编写,随处使用”。

人们嘲弄地将 AWT 称为 “ 一次编写, 随处调试”。

在 1996 年,Netscape 创建了一种称为 IFC ( Internet Foundation Class) 的 GUI 库, 它采 用了与 AWT 完全不同的工作方式。它将按钮、菜单这样的用户界面元素绘制在空白窗口上, 而对等体只需要创建和绘制窗口。因此,Netscape 的 IFC组件在程序运行的所有平台上的外 观和动作都一样。Sun 与 Netscape 合作完善了这种方式, 创建了一个名为Swing 的用户界面 库。Swing可作为Java 1.1 的扩展部分使用,现已成为 Java SE 1.2标准库的一部分。

[注]  Swing 没有完全替代 AWT, 而是基于 AWT 架构之上。Swing 仅仅提供了能力更 加强大的用户界面组件。 尤其在采用 Swing 编写的程序中,还需要使用基本的 AWT 处 理事件。从现在开始,Swing 是指 “ 被绘制的” 用户界面类;AWT 是指像事件处理这样 的窗口工具箱的底层机制。

在用户屏幕上显示基于 Swing 用户界面的元素要比显示 AWT 的基于对等体组件 的速度慢一些。鉴于以往的经验, 对于任何一台现代的计算机来说, 微小的速度差别无妨大 碍。另外,由于下列几点无法抗拒的原因,人们选择 Swing:

•Swing 拥有一个丰富、 便捷的用户界面元素集合。

•Swing 对底层平台依赖的很少,因此与平台相关的 bug 很少。

•Swing 给予不同平台的用户一致的感觉。

不过,上面第三点存在着一个潜在的问题: 如果在所有平台上用户界面元素看起来都一 样,那么它们就有可能与本地控件不一样,而这些平台的用户对此可能并不熟悉。

创建框架

在 Java 中,顶层窗口(就是没有包含在其他窗口中的窗口)被称为框架(frame)。在 AWT 库中有一个称为 Frame 的类, 用于描述顶层窗口。这个类的 Swing 版本名为JFrame, 它扩展于 Frame 类。JFrame 是极少数几个不绘制在画布上的 Swing 组件之一。因此,它的修 饰部件(按钮、标题栏、图标等)由用户的窗口系统绘制, 而不是由 Swing绘制。

[警告]  绝大多数 Swing 组件类都以“ J” 开头, 例如,JButton、JFrame 等。在 Java 中有 Button 和 Frame 这样的类, 但它们属于 AWT 组件。如果偶然地忘记书写“ J”,程序仍 然可以进行编译和运行,但是将 Swing 和 AWT 组件混合在一起使用将会导致视觉和行 为的不一致。

在本节中,将介绍有关 Swing 的 JFrame 的常用方法。程序清单 10-1 给出了一个在屏幕 中显示一个空框架的简单程序, 如图 10-5 所示。

//程序清单 10-1 simpleframe/SimpleFrameTest.java 
package simpleFrame;
​
import java.awt.*;
import javax.swing.*;
​
/**
 * @version 1.32 2007-06-12
 * @author Cay Horstmann
 */
public class SimpleFrameTest
{
   public static void main(String[] args)
   {
      EventQueue.invokeLater(new Runnable()
         {
            public void run()
            {
               SimpleFrame frame = new SimpleFrame();
               frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
               frame.setVisible(true);
            }
         });
   }
}
​
class SimpleFrame extends JFrame
{
   private static final int DEFAULT_WIDTH = 300;
   private static final int DEFAULT_HEIGHT = 200;
​
   public SimpleFrame()
   {
      setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
   }
}

 


Swing 类位于javax.swing 包中。包名 javax 表示这是 一个 Java 扩展包, 而不是核心包。出于历史原因 Swing 类被认为是一个扩展。不过从 1.2 版本开始,在每个 Java SE实现中都包含它。

在每个 Swing 程序中,有两个技术问题需要强调。 首先,所有的 Swing 组件必须由事件分派线程(event dispatch thread) 进行配置,线程将 鼠标点击和按键控制转移到用户接口组件。下面的代码片断是事件分派线程中的执行代码:

EventQueue.invokeLater(0 -> 
                       { 
                           statements 
                       });
 
[注]  许多 Swing 程序并没有在事件分派线程中初始化用户界面。在主线程中完成初始 化是通常采用的方式。遗憾的是, 由于 Swing 组件十分复杂,JDK 的程序员无法保证这 种方式的安全性。 虽然发生错误的概率非常小, 但任何人不愿意成为遭遇这个问题的不 幸者之一。即使代码看起来有些神秘,也最好能够保证其正确性。

可以调用 frame.setUndecorated(true) 关闭所有框架装饰。

框架定位

JFrame 类本身只包含若干个改变框架外观的方法。当然,通过继承, 从 JFrame 的各个 超类中继承了许多用于处理框架大小和位置的方法 其中最重要的有下面几个:

•setLocation 和 setBounds 方法用于设置框架的位置。

•setlconlmage 用于告诉窗口系统在标题栏、任务切换窗口等位置显示哪个图标。

•setTitle 用于改变标题栏的文字。

•setResizable 利用一个 boolean 值确定框 架的大小是否允许用户改变。

图 10-6 给出了 JFrame 类的继承层次。

框架属性

组件类的很多方法是以获取 / 设置方法对形式出现的, 例如,Frame 类的下列方法:

public String getTitle()
​
public void setTitle(String title) 

 

这样的一个获取 / 设置方法对被称为一种属性。属性包含属性名和类型。将 get 或 set 之后的 第一个字母改为小写字母就可以得到相应的属性名。例如, Frame 类有一个名为 title且类型 为 String 的属性。

从概念上讲,title 是框架的一个属性。当设置这个属性时,希望这个标题能够改变用户 屏幕上的显示。当获取这个属性时, 希望能够返回已经设置的属性值。

针对 get/set 约定有一个例外: 对于类型为 boolean 的属性, 获取方法由 is开头。例如, 下面两个方法定义了 locationByPlatform 属性:

 public boolean islocationByPIatforn()
 public void setLocationByPIatforra(boolean b)

 

确定合适的框架大小

要记住: 如果没有明确地指定框架的大小,所有框架的默认值为 0x 0 像素。

为了得到屏幕的大小, 需要按照下列步骤操作。调用 Toolkit 类的静态方法 getDefaultToolkit 得到一个 Toolkit 对象(Toolkit 类包含很多与本地窗口系统打交道的方法)。然后,调用 getScreenSize方法,这个方法以 Dimension对象的形式返回屏幕的大小。Dimension 对象同时 用公有实例变量 width 和 height 保存着屏幕的宽度和高度。下面是相关的代码:

Toolkit kit = Toolkit.getDefaultToolkit(); 
Dimension screenSize= kit.getScreenSize(); 
int screenWidth = screenSize.width; 
int screenHeight = screenSize.height;

 

下面,将框架大小设定为上面取值的 50%,然后,告知窗口系统定位框架:

setSize(screenWidth / 2, screenHeight / 2); 
setLocationByPlatform(true); 

 

另外,还提供一个图标。由于图像的描述与系统有关,所以需要再次使用工具箱加载图 像。然后,将这个图像设置为框架的图标。

Image img = new InageIcon("icon.gif").getImage(); 
​
setIconImage(img);

 

程序清单 10-2 是完整的程序。当运行程序时,请注意看 “ Core Java” 图标。 下面是为了处理框架给予的一些提示:

•如果框架中只包含标准的组件, 如按钮和文本框,那么可以通过调用pack方法设置 框架大小。框架将被设置为刚好能够放置所有组件的大小。在通常情况下, 将程序的 主框架尺寸设置为最大。可以通过调用下列方法将框架设置为最大。

frame.setExtendedState(Frame.MAXIMIZEDJOTH):

•牢记用户定位应用程序的框架位置、 重置框架大小,并且在应用程序再次启动时恢复 这些内容是一个不错的想法。在第 13 章中将会介绍如何运用 API 的参数选择达到这 个目的。

•GraphicsDevice 类还允许以全屏模式执行应用。

//程序清单 10-2 sizedFrame/SizedFrameTest.java
package sizedFrame;
​
import java.awt.*;
import javax.swing.*;
​
/**
 * @version 1.32 2007-04-14
 * @author Cay Horstmann
 */
public class SizedFrameTest
{
   public static void main(String[] args)
   {
      EventQueue.invokeLater(new Runnable()
         {
            public void run()
            {
               JFrame frame = new SizedFrame();
               frame.setTitle("SizedFrame");
               frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
               frame.setVisible(true);
            }
         });
   }
}
​
class SizedFrame extends JFrame
{
   public SizedFrame()
   {
      // get screen dimensions
​
      Toolkit kit = Toolkit.getDefaultToolkit();
      Dimension screenSize = kit.getScreenSize();
      int screenHeight = screenSize.height;
      int screenWidth = screenSize.width;
​
      // set frame width, height and let platform pick screen location
​
      setSize(screenWidth / 2, screenHeight / 2);
      setLocationByPlatform(true);
​
      // set frame icon
​
      Image img = new ImageIcon("icon.gif").getImage();
      setIconImage(img);      
   }
}

 


在组件中显示信息

JFrame 的结构相当复杂。在图 10-8中给出了 JFrame 的 结构。可以看到,在 JFrame 中有四层面板。其中的根面板、层级面板和玻璃面板人们并不太关心;它们是用来组织菜单栏和内容窗格以及实现观感的。 Swing 程序员最关心的是内容窗格(contentpane)。在设计框架的时候, 要使用下列代码将所 有的组件添加到内容窗格中:

Container contentPane = frame.getContentPane(); 
​
Component c = . . .; 
​
contentPane.add(c);

 

在 Java SE 1.4 及以前的版本中,JFrame 类中的 add方法抛出了一个异常信息“ Do not use JFrame.add().Use JFrame.getContentPaneQ.add instead”。如今, JFrame.add方法不再显示这些提示信息,只是简单地调用内容窗格的 add,

因此,可以直接调用

frame.add(c);

在这里,打算将一个绘制消息的组件添加到框架中。绘制一个组件,需要定义一个扩展 JComponent 的类,并覆盖其中的 paintComponent 方法。

paintComponent 方法有一个 Graphics 类型的参数, 这个参数保存着用于绘制图像和文本 的设置, 例如,设置的字体或当前的颜色。在 Java中,所有的绘制都必须使用 Graphics对 象, 其中包含了绘制图案、 图像和文本的方法。

下列代码给出了如何创建一个能够进行绘制的组件:

class MyComponent extends JComponent { 
    public void paintComponent(Graphics g) {
        codefordrawing 
    } 
} 

 

无论何种原因, 只要窗口需要重新绘图, 事件处理器就会通告组件,从而引发执行所有 组件的 paintComponent 方法。

一定不要自己调用 paintComponent 方法。在应用程序需要重新绘图的时候, 这个方法将 被自动地调用,不要人为地干预这个自动的处理过程。

 

显示文本是一种特殊的绘图。在 Graphics 类中有一个 drawstring方法, 调用的语法格 式为:

g.drawString(text, x, y)

在这里, 打算在原始窗口大约水平 1/4, 垂直 1/2 的位置显示字符串“ Not a Hello, World program" o 现在,尽管不知道应该如何度量这个字符串的大小, 但可以将字符串的开始位置 定义在坐标(75, 100 )。这就意味着字符串中的第一个字符位于从左向右 75 个像素, 从上向下 100 个像素的位置(实际上, 文本的基线位于像素 100 的位置, 有关文本的度量方式将 在稍后阐述)。因此, paintComponent 方法的书写内容如下所示:

public class NotHelloWorldComponent extends JComponent { 
    public static final int MESSACE_X = 75; 
    public static final int MESSACE. Y = 100;
    public void paintComponent(Graphics g) { 
        g.drawString("Not a Hello World program", MESSACE_X, MESSACE_Y);
    }
    ...
}

 

程序清单 10-3 给出了完整的代码。

//程序清单 10-3 notHelloWorid/NotHelloWorld.java 
package notHelloWorld;
​
import javax.swing.*;
import java.awt.*;
​
/**
 * @version 1.32 2007-06-12
 * @author Cay Horstmann
 */
public class NotHelloWorld
{
   public static void main(String[] args)
   {
      EventQueue.invokeLater(new Runnable()
         {
            public void run()
            {
               JFrame frame = new NotHelloWorldFrame();
               frame.setTitle("NotHelloWorld");
               frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
               frame.setVisible(true);
            }
         });
   }
}
​
/**
 * A frame that contains a message panel
 */
class NotHelloWorldFrame extends JFrame
{
   public NotHelloWorldFrame()
   {
      add(new NotHelloWorldComponent());
      pack();
   }
}
​
/**
 * A component that displays a message.
 */
class NotHelloWorldComponent extends JComponent
{
   public static final int MESSAGE_X = 75;
   public static final int MESSAGE_Y = 100;
​
   private static final int DEFAULT_WIDTH = 300;
   private static final int DEFAULT_HEIGHT = 200;
​
   public void paintComponent(Graphics g)
   {
      g.drawString("Not a Hello, World program", MESSAGE_X, MESSAGE_Y);
   }
   
   public Dimension getPreferredSize() { return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT); }
}

 

处 理 2D 图形

自从 Java 版本 1.0 以来, Graphics类就包含绘制直线、矩形和楠圆等方法。但是,这些 绘制图形的操作能力非常有限。例如, 不能改变线的粗细,不能旋转这些图形。

要想使用 Java 2D 库绘制图形,需要获得一个 Graphics2D 类对象。这个类是 Graphics 类 的子类。自从 Java SE 2 版本以来,paintComponent 方法就会自动地获得一个 Graphics2D 类 对象,我们只需要进行一次类型转换就可以了。如下所示:

public void paintComponent(Craphics g) { 
    Craphics2D g2 = (Graphics2D) g;
    ...
}

 

Java 2D 库采用面向对象的方式将几何图形组织起来。包含描述直线、矩形的椭圆的类:

Line2D

Rectang1e2D

Ellipse2D

这些类全部实现了 Shape 接口。

要想绘制图形,首先要创建一个实现了 Shape 接口的类的对象,然后调用 GraphicS2D类 中的 draw方法。例如,

Rectangle2D rect = . . .;

g2.draw(rect);

当 创 建 一 个 Rectangle2D.Float 对 象 时, 应 该 提 供 float 型 数 值 的 坐 标。 而 创 建 Rectangle2D. Double 对象时,应该提供 double 型数值的坐标。

Rectangle2D.FIoat floatRect -new Rectangle2D.FIoat(10.OF, 25.OF, 22.SF, 20.OF); 
​
Rectangle2D.Double doubleRect = new Rectangle2D.Oouble(10.0, 2S.0, 22.5, 20.0);

 

实 际 上, 由 于 Rectangle2D.Float 和 Rectangle2D.Double 都 扩 展 于 Rectangle2D 类, 并 且子类只覆盖了 RectangldD 超类中的方法, 所以没有必要记住图形类型。可以直接使用 Rectangle2D 变量保存矩形的引用。

Rectangle2D floatRect = new Rectangle2D.FIoat(10.OF, 25.OF, 22.5F, 20.OF);
​
Rectangle2D doubleRect = new Rectangle2D.Double(10.0, 2S.0, 22.5, 20.0); 

 

也就是说, 只有在构造图形对象时,才需要使用烦人的内部类。

构造参数表示矩形的左上角位置、宽和髙。

实际上, Rectangle2D.Float 类包含了一个不是由 Rectangle2D 继承而来的附加方法 setRect(float x, float y , float h, float w)。如果将 Rectangle2D.Float 的引用存储在 Rectangle2D 变量中, 那就会失去这个方法。但是,这也没有太大关系, 因为在 Rectangle2D 中有一个 参教为 double 类型的 setRect 方法。

[提示]  :直接使用 Double 图形类可以避免处理 float 类型的值, 然而如果需要创建上千个 图形对象,还是应该考虑使用 Float 类,这样可以节省存储空间。

 

程序清单 10-4中的程序绘制了一个矩形;这个矩形的内接椭圆;矩形的对角线以及以矩 形中心为圆点的圆。图 10-12显示了结果。

//程序清单 10-4 draw/DrawTest.java
package draw;
​
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
​
/**
 * @version 1.32 2007-04-14
 * @author Cay Horstmann
 */
public class DrawTest
{
   public static void main(String[] args)
   {
      EventQueue.invokeLater(new Runnable()
         {
            public void run()
            {
               JFrame frame = new DrawFrame();
               frame.setTitle("DrawTest");
               frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
               frame.setVisible(true);
            }
         });
   }
}
​
/**
 * A frame that contains a panel with drawings
 */
class DrawFrame extends JFrame
{
   public DrawFrame()
   {      
      add(new DrawComponent());
      pack();
   }
}
​
/**
 * A component that displays rectangles and ellipses.
 */
class DrawComponent extends JComponent
{
   private static final int DEFAULT_WIDTH = 400;
   private static final int DEFAULT_HEIGHT = 400;
​
   public void paintComponent(Graphics g)
   {
      Graphics2D g2 = (Graphics2D) g;
​
      // draw a rectangle
double leftX = 100;
      double topY = 100;
      double width = 200;
      double height = 150;
​
      Rectangle2D rect = new Rectangle2D.Double(leftX, topY, width, height);
      g2.draw(rect);
​
      // draw the enclosed ellipse
​
      Ellipse2D ellipse = new Ellipse2D.Double();
      ellipse.setFrame(rect);
      g2.draw(ellipse);
​
      // draw a diagonal line
​
      g2.draw(new Line2D.Double(leftX, topY, leftX + width, topY + height));
​
      // draw a circle with the same center
double centerX = rect.getCenterX();
      double centerY = rect.getCenterY();
      double radius = 150;
​
      Ellipse2D circle = new Ellipse2D.Double();
      circle.setFrameFromCenter(centerX, centerY, centerX + radius, centerY + radius);
      g2.draw(circle);
   }
   
   public Dimension getPreferredSize() { return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT); }
}
​

 

使用颜色

使用 Gr叩hics2D 类的 setPaint 方法可以为图形环境上的所有后续的绘制操作选择颜色。 例如:

g2.setPaint(Color.RED);

g2.drawString("Warning!", 100,100);

只需要将调用 draw 替换为调用 fill 就可以用一种颜色填充一个封闭图形(例如: 矩形或 椭圆)的内部:

Rectangle2D rect = . . .;

g2.setPaint(Color.RED);

g2.fi11(rect); // fills rect with red

要想绘制多种颜色, 就需要按照选择颜色、 绘制图形、 再选择另外一种颜色、再绘制图 形的过程实施。

[注]  fill 方法会在右侧和下方少绘制一个像素。 例如, 如果绘制一个 new Rectangle2D. Double(0, 0, 10, 20), 绘制的矩形将包括 x = 10 和 y = 20 的像素。如果填充这个矩形, 则 不会绘制 x = 10 和 y = 20 的像素。

Color 类用于定义颜色。在java.awt.Color 类中提供了 13 个预定义的常量, 它们分別表 示 13 种标准颜色。

BLACK, BLUE, CYAN, DARK.GRAY, CRAY, GREEN, LIGHT_CRAY, MACENTA, ORANGE, PINK, RED, WHITE, YELLOW

可以通过提供红、绿和蓝三色成分来创建一个 Color 对象,以达到定制颜色的目的。三 种颜色都是用 0 ~ 255(也就是一个字节)之间的整型数值表示,调用 Color 的构造器格式为:

Color(int redness, int greenness, int blueness)

下面是一个定制颜色的例子:

g2.setPaint(new Color(0, 128, 128)); // a dull blue-green 
​
g2.drawString("Welcome!",75, 125); 

 

要想设置背景颜色, 就需要使用 Component 类中的 setBackground方法。Component 类 是 JComponent 类的祖先。

MyComponent p = new HyComponent();
​
p.setBackground(Color.PINK); 

 

另外,还有一个 setForeground方法,它是用来设定在组件上进行绘制时使用的默认颜色。

 

文本使用特殊字体

要想知道某台特定计算机上允许使用的字体, 就需要调用 GraphicsEnvironment 类中的 getAvailableFontFamilyNames方法。这个方法将返回一个字符型数组, 其中包含了所有可用 的字体名。GraphicsEnvironment 类描述了用户系统的图形环境, 为了得到这个类的对象,需要 调用静态的 getLocalGraphicsEnvironment 方法。下面这个程序将打印出系统上的所有字体名:

import java.awt.*; 

public class ListFonts { 
    public static void main(String[] args) { 
        String[] fontNames = GraphicsEnvironment .getLocalGraphicsEnvironmentp .getAvailableFontFamilyNames(); 
        for (String fontName : fontNames) 
            System.out.println(fontName); 
    } 
} 

 

为了创建一个公共基准, AWT 定义了五个逻辑(logical) 字体名:

SansSerif

Serif

Monospaced

Dialog

Dialoglnput

些字体将被映射到客户机上的实际字体。例如,在 Windows 系统中, SansSerif将被映 射到 Arial。

要想使用某种字体绘制字符, 必须首先利用指定的字体名、字体风格和字体大小来创建— 个 Font 类对象。下面是构造一个 Font 对象的例子:

Font sansbold14 = new Font(°SansSerif", Font.BOLD, 14);

第三个参数是以点数目计算的字体大小。点数目是排版中普遍使用的表示字体大小的单 位,每英寸包含 72 个点。

为了说明位置是正确的, 示例程序绘制了基线和包围 这个字符串的矩形。图 10-14 给出了屏幕显示结果。程序图 10-M 绘制基线和字符串边框 清单 10-5 中是相应的代码。

//程序清单 font/FontTest.java
package font;
​
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import javax.swing.*;
​
/**
 * @version 1.33 2007-04-14
 * @author Cay Horstmann
 */
public class FontTest
{
   public static void main(String[] args)
   {
      EventQueue.invokeLater(new Runnable()
         {
            public void run()
            {
               JFrame frame = new FontFrame();
               frame.setTitle("FontTest");
               frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
               frame.setVisible(true);
            }
         });
   }
}
​
/**
 * A frame with a text message component
 */
class FontFrame extends JFrame
{
   public FontFrame()
   {      
      add(new FontComponent());
      pack();
   }
}
​
/**
 * A component that shows a centered message in a box.
 */
class FontComponent extends JComponent
{
   private static final int DEFAULT_WIDTH = 300;
   private static final int DEFAULT_HEIGHT = 200;
​
   public void paintComponent(Graphics g)
   {
      Graphics2D g2 = (Graphics2D) g;
​
      String message = "Hello, World!";
​
      Font f = new Font("Serif", Font.BOLD, 36);
      g2.setFont(f);
​
      // measure the size of the message
​
      FontRenderContext context = g2.getFontRenderContext();
      Rectangle2D bounds = f.getStringBounds(message, context);
​
      // set (x,y) = top left corner of text
double x = (getWidth() - bounds.getWidth()) / 2;
      double y = (getHeight() - bounds.getHeight()) / 2;
​
      // add ascent to y to reach the baseline
double ascent = -bounds.getY();
      double baseY = y + ascent;
​
      // draw the message
​
      g2.drawString(message, (int) x, (int) baseY);
​
      g2.setPaint(Color.LIGHT_GRAY);
​
      // draw the baseline
​
      g2.draw(new Line2D.Double(x, baseY, x + bounds.getWidth(), baseY));
​
      // draw the enclosing rectangle
​
      Rectangle2D rect = new Rectangle2D.Double(x, y, bounds.getWidth(), bounds.getHeight());
      g2.draw(rect);
   }
   
   public Dimension getPreferredSize() { return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT); }
}
​

 

显示图像

一旦图像保存在本地文件或因特网的某个位置上,就可以将它们读到 Java 应用程序中, 并在 Graphics 对象上进行显示。读取图像有很多方法。在这里我们使用你之前已经见过的 Imagelcon类:

Image image = new Imagelcon(filename) .getlmage();

 

这里的变量 image 包含了一个封装图像数据的对象引用。可以使用 Graphics类的 drawlmage 方法将图像显示出来。

public void paintComponent(Graphics g) { 
    ...
    g.drawlmage(image, x, y, null); 
} 

 

程序清单 10-6 又前进了一步, 它在一个窗口中平铺显 示了一幅图像。 这里采 用 paintComponent 方法来实现平铺显示。它的基本过程为: 先在左上角显示图像的一个拷贝, 然后使用 copyArea 将其 拷贝到整个窗口:

for (int i = 0; i * imageWidth <= getWidthO; i++) 
   for (int j = 0; j * imageHeight <= getHeightO; j++)
       if (i + j > 0)
           g.copyArea(0, 0, imageWidth, imageHeight,i * imageWidth, j * imageHeight);

程序清单 10-6 列出了图像显示程序的完整代码。

//程序清单 10-6 image/lmageTest.java
package image;
​
import java.awt.*;
import javax.swing.*;
​
/**
 * @version 1.33 2007-04-14
 * @author Cay Horstmann
 */
public class ImageTest
{
   public static void main(String[] args)
   {
      EventQueue.invokeLater(new Runnable()
         {
            public void run()
            {
               JFrame frame = new ImageFrame();
               frame.setTitle("ImageTest");
               frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
               frame.setVisible(true);
            }
         });
   }
}
​
/**
 * A frame with an image component
 */
class ImageFrame extends JFrame
{
   public ImageFrame()
   {
      add(new ImageComponent());
      pack();
   }
}
​
/**
 * A component that displays a tiled image
 */
class ImageComponent extends JComponent
{
   private static final int DEFAULT_WIDTH = 300;
   private static final int DEFAULT_HEIGHT = 200;
​
   private Image image;
​
   public ImageComponent()
   {
      image = new ImageIcon("blue-ball.gif").getImage();
   }
​
   public void paintComponent(Graphics g)
   {
      if (image == null) return;
​
      int imageWidth = image.getWidth(this);
      int imageHeight = image.getHeight(this);
​
      // draw the image in the upper-left corner
​
      g.drawImage(image, 0, 0, null);
      // tile the image across the component
for (int i = 0; i * imageWidth <= getWidth(); i++)
         for (int j = 0; j * imageHeight <= getHeight(); j++)
            if (i + j > 0) 
               g.copyArea(0, 0, imageWidth, imageHeight, i * imageWidth, j * imageHeight);
   }
   
   public Dimension getPreferredSize() { return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT); }
}

 


 

posted on 2020-08-16 20:21  ♌南墙  阅读(341)  评论(0)    收藏  举报