文档

Java™ 教程
隐藏目录
如何使用图标
路径: 使用Swing创建GUI
课程: 使用Swing组件

如何使用图标

许多Swing组件,例如标签、按钮和选项卡窗格,可以用一个图标进行装饰,即一个固定大小的图片。图标是一个遵循Icon接口的对象。Swing提供了一个特别有用的Icon接口实现:ImageIcon,它可以根据GIF、JPEG或PNG图片绘制图标。

下面是一个应用程序的快照,其中有三个标签,其中两个用图标进行了装饰:

使用图像图标装饰标签的示例。

该程序使用一个图像图标来包含和绘制黄色斑点。一条语句创建图像图标,另外两条语句将图像图标添加到两个标签上:

ImageIcon icon = createImageIcon("images/middle.gif",
                                 "一个漂亮但没有意义的斑点");
label1 = new JLabel("图像和文本", icon, JLabel.CENTER);
...
label3 = new JLabel(icon);

createImageIcon方法(在上述片段中使用)是我们在许多代码示例中使用的方法。它会找到指定的文件并返回该文件的ImageIcon,如果找不到该文件则返回null。这是一个典型的实现:

/** 返回一个ImageIcon,如果路径无效则返回null。 */
protected ImageIcon createImageIcon(String path,
                                           String description) {
    java.net.URL imgURL = getClass().getResource(path);
    if (imgURL != null) {
        return new ImageIcon(imgURL, description);
    } else {
        System.err.println("找不到文件: " + path);
        return null;
    }
}

在上述片段中,ImageIcon构造函数的第一个参数是相对于当前类的位置,将被解析为绝对URL。description参数是一个字符串,允许辅助技术帮助视力受损的用户理解图标传达的信息。

通常,应用程序会提供自己的一组用作应用程序一部分的图像,就像我们的许多演示中使用的图像一样。您应该使用ClassgetResource方法来获取图像的路径。这样应用程序可以验证图像是否可用,并在图像不可用时提供合理的错误处理。当图像不是应用程序的一部分时,不应使用getResource,而应直接使用ImageIcon构造函数。例如:

ImageIcon icon = new ImageIcon("images/middle.gif",
                               "一个漂亮但没有意义的斑点");

当您在ImageIcon构造函数中指定文件名或URL时,会阻塞处理直到图像数据完全加载或数据位置被证明无效。如果数据位置无效(但非空),仍然会成功创建ImageIcon,但它没有大小,因此不会绘制任何内容。如createImageIcon方法所示,建议在将URL传递给ImageIcon构造函数之前,首先验证URL是否指向现有文件。这样可以在文件不存在时进行优雅的错误处理。如果在加载图像时需要更多信息,可以通过调用setImageObserver方法在图像图标上注册一个观察者。

在内部,每个图像图标使用一个Image对象来保存图像数据。

本节的其余部分涵盖以下主题:

更复杂的图像图标示例

这是一个使用六个图像图标的应用程序。其中五个显示缩略图像,第六个显示全尺寸照片。

IconDemo应用程序的初始视图。

请尝试以下操作: 
  1. 点击“启动”按钮以使用Java™ Web Start运行IconDemo(下载JDK 7或更高版本)。或者,如果要编译和运行示例,请参考示例索引

    启动IconDemo示例
  2. 点击任何缩略图像以查看全尺寸照片。

  3. 将鼠标悬停在照片上。将显示包含照片标题的工具提示。

IconDemoApp演示了以下方式使用图标:

照片通过loadimages.execute在一个单独的线程中加载。稍后在本节中会展示loadimages的代码。

ThumbnailAction类是IconDemoApp.java中的一个内部类,它是AbstractAction的子类,用于管理我们的全尺寸图标、缩略图版本和描述。当调用actionPerformed方法时,全尺寸图像将加载到主显示区域。每个按钮都有自己的ThumbnailAction实例,指定要显示的不同图像。

/**
 * Action class that shows the image specified in it's constructor.
 */
private class ThumbnailAction extends AbstractAction{

    /**
     *The icon if the full image we want to display.
     */
    private Icon displayPhoto;

    /**
     * @param Icon - The full size photo to show in the button.
     * @param Icon - The thumbnail to show in the button.
     * @param String - The description of the icon.
     */
    public ThumbnailAction(Icon photo, Icon thumb, String desc){
        displayPhoto = photo;

        // The short description becomes the tooltip of a button.
        putValue(SHORT_DESCRIPTION, desc);

        // The LARGE_ICON_KEY is actually the key for setting the
        // icon when an Action is applied to a button.
        putValue(LARGE_ICON_KEY, thumb);
    }

    /**
     * Shows the full image in the main area and sets the application title.
     */
    public void actionPerformed(ActionEvent e) {
        photographLabel.setIcon(displayPhoto);
        setTitle("图标演示: " + getValue(SHORT_DESCRIPTION).toString());
    }
}

使用getResource加载图像

通常,图像图标的数据来自图像文件。您的应用程序的类和图像文件可能在文件服务器上以多种方式配置。您可能将类文件和图像文件放在一个JAR文件中,或者将它们放在不同的JAR文件中。以下图示说明了这些文件可能配置的几种方式:

在父目录下显示MyDemo.class和images/myImage.png的示意图 在父目录下显示MyDemo.class和image.jar的示意图
类文件与包含图像文件(PNG格式)的图像目录相邻。 类文件与JAR文件在同一目录下。JAR文件包含一个名为images的目录,其中包含所有图像文件。
在父目录下显示MyDemo.jar和image.jar的示意图 在同一JAR文件中显示MyDemo.class和images/myImage.png的示意图
类文件在一个JAR文件中,图像文件在另一个JAR文件中。 类文件和图像文件在同一个JAR文件中。

如果你正在编写一个真实的应用程序,很可能(也推荐)将你的文件放入一个包中。关于包的更多信息,请参阅创建和使用包学习Java语言 章节。这里有一些可能的配置,使用一个名为"omega"的包:

显示omega包和MyDemo.class和image/myImage.png的示意图 显示omega包和MyDemo.class和image.jar的示意图
类文件在名为omega的目录中。图像在omega/images目录中。 类文件在omega目录中。图像在JAR文件中,不在omega目录内,但是使用omega/images层次结构创建。
显示包含omega/MyDemo.class和omega/images/myImage.png的omega.jar的示意图
一个包含类文件在omega目录下和图像文件在omega/images目录下的大型JAR文件。

所有七个示意图显示的配置都是有效的,相同的代码读取图像:

java.net.URL imageURL = myDemo.class.getResource("images/myImage.gif");
...
if (imageURL != null) {
    ImageIcon icon = new ImageIcon(imageURL);
}

getResource 方法会导致类加载器在程序的类路径中查找目录和JAR文件,一旦找到所需的文件,就会返回一个URL。在示例中,MyDemo程序尝试从omega类中加载images/myImage.png文件。类加载器在程序的类路径中查找/omega/images/myImage.png。如果类加载器找到了该文件,它将返回包含该文件的JAR文件或目录的URL。如果类路径中的另一个JAR文件或目录包含images/myImage.png文件,类加载器将返回第一个包含该文件的实例。

以下是三种指定类路径的方式:

大多数Swing教程示例将图像放在包含示例类文件的目录下的images目录中。当我们为示例创建JAR文件时,保持相同的相对位置,尽管通常我们将类文件和图像JAR文件放在不同的JAR文件中。无论类和图像文件在文件系统中的位置如何 - 在一个JAR文件中还是在多个JAR文件中,在命名包中还是在默认包中 - 相同的代码都使用getResource来找到图像文件。

有关更多信息,请参见以位置无关方式访问资源应用程序开发注意事项

将图像加载到Applet中

Applet通常从提供Applet的计算机加载图像数据。在APPLET标签中,您可以指定有关Applet中使用的图像的信息。有关APPLET标签的更多信息,请参见使用APPLET标签

在加载图像图标时提高感知性能

由于访问照片图像可能很慢,IconDemoApp.java使用SwingWorker来改善用户感知的程序性能。

背景图像加载 — 程序使用javax.swing.SwingWorker对象在后台线程中加载每个照片图像并计算其缩略图。使用SwingWorker可以防止程序在加载和缩放图像时出现冻结。

以下是处理每个图像的代码:

/**
 * SwingWorker类在后台线程中加载图像,并在准备显示新图像时调用publish。
 *
 * 我们在doInBackground()中使用Void作为第一个SwingWorker参数,因为我们不需要从doInBackground()中返回任何内容。
 */
private SwingWorker<Void, ThumbnailAction> loadimages = new SwingWorker<Void, ThumbnailAction>() {

    /**
     * 创建目标图像文件的全尺寸和缩略图版本。
     */
    @Override
    protected Void doInBackground() throws Exception {
        for (int i = 0; i < imageCaptions.length; i++) {
            ImageIcon icon;
            icon = createImageIcon(imagedir + imageFileNames[i], imageCaptions[i]);

            ThumbnailAction thumbAction;
            if(icon != null){

                ImageIcon thumbnailIcon = new
                     ImageIcon(getScaledImage(icon.getImage(), 32, 32));


                thumbAction = new ThumbnailAction(icon, thumbnailIcon, imageCaptions[i]);

            } else {
                // 图像加载失败,加载占位符
                thumbAction = new ThumbnailAction(placeholderIcon, placeholderIcon, imageCaptions[i]);
            }
            publish(thumbAction);
        }
        // 不幸的是我们必须返回一些东西,而只有null对于void类型的返回值是有效的。
        return null;
    }

    /**
     * 处理所有加载的图像。
     */
    @Override
    protected void process(List<ThumbnailAction> chunks) {
        for (ThumbnailAction thumbAction : chunks) {
            JButton thumbButton = new JButton(thumbAction);
            // 在最后一个glue之前添加新按钮
            // 这样可以使按钮在工具栏中居中
            buttonBar.add(thumbButton, buttonBar.getComponentCount() - 1);
        }
    }
};

SwingWorker在后台线程中调用doInBackground方法。该方法将全尺寸图像、缩略图尺寸图像和标题放入ThumbnailAction对象中。然后,SwingWorker将ThumbnailAction传递给process方法。process方法在事件分派线程上执行,并通过向工具栏添加按钮来更新GUI。JButton具有一个接受动作对象的构造函数。动作对象确定了按钮的许多属性。在我们的例子中,按钮的图标、标题和按下按钮时执行的操作都由ThumbnailAction确定。

开销 - 此程序最终将所有源图像加载到内存中。这在所有情况下可能都不是可取的。加载大量非常大的文件可能导致程序分配非常大的内存。应该注意管理加载的图像数量和大小。

与所有性能相关的问题一样,此技术在某些情况下适用,而在其他情况下不适用。此外,这里描述的技术旨在改善程序的感知性能,但不一定影响其真实性能。

创建自定义图标实现

createImageIcon方法找不到图像时,它会返回null,但此时程序应该怎么做呢?一种可能性是忽略该图像并继续。另一种选择是在无法加载真实图像时提供某种默认图标进行显示。再次调用createImageIcon可能会导致另一个null,所以使用它不是一个好主意。相反,我们可以创建一个自定义的Icon实现。

MissingIcon的示例。

您可以在MissingIcon.java中找到自定义图标类的实现。以下是其代码中有趣的部分:

/**
 * "缺失图标"是一个带有黑色边框和红色x的白色方框。
 * 当从外部位置加载图标时出现问题时,它用于显示某些内容。
 *
 * 作者:Collin Fagan
 */
public class MissingIcon implements Icon{

    private int width = 32;
    private int height = 32;

    private BasicStroke stroke = new BasicStroke(4);

    public void paintIcon(Component c, Graphics g, int x, int y) {
        Graphics2D g2d = (Graphics2D) g.create();

        g2d.setColor(Color.WHITE);
        g2d.fillRect(x +1 ,y + 1,width -2 ,height -2);

        g2d.setColor(Color.BLACK);
        g2d.drawRect(x +1 ,y + 1,width -2 ,height -2);

        g2d.setColor(Color.RED);

        g2d.setStroke(stroke);
        g2d.drawLine(x +10, y + 10, x + width -10, y + height -10);
        g2d.drawLine(x +10, y + height -10, x + width -10, y + 10);

        g2d.dispose();
    }

    public int getIconWidth() {
        return width;
    }

    public int getIconHeight() {
        return height;
    }
}

paintIcon方法接收一个Graphics对象。Graphics对象使paintIcon方法可以访问整个Java2D API。有关绘制和Java2D的更多信息,请参见执行自定义绘制

以下代码演示了在SwingWorkerdoInBackground方法中使用MissingIcon类的示例。

private MissingIcon placeholderIcon = new MissingIcon();

...
if(icon != null) {
    ...

} else {
    // 图片由于某种原因加载失败
    // 所以加载一个占位符
    thumbAction = new ThumbnailAction(placeholderIcon, placeholderIcon, imageCaptions[i]);
}

使用自定义图标有一些影响:

Image Icon API

以下表格列出了常用的ImageIcon构造函数和方法。请注意,ImageIcon不是JComponent甚至不是Component的子类。

使用图像图标的API可分为以下类别:

设置、获取和绘制图像图标的图像ImageIcon()
ImageIcon(byte[])
ImageIcon(byte[], String)
ImageIcon(Image)
ImageIcon(Image, String)
ImageIcon(String)
ImageIcon(String, String)
ImageIcon(URL)
ImageIcon(URL, String)ImageIconjava.awt.ImagesetDescription辅助技术void setImage(Image)
Image getImage()void paintIcon(Component, Graphics, int, int)ComponentComponentintURL getResource(String)
java.lang.ClassLoader使用getResource加载图像InputStream getResourceAsStream(String)
java.lang.ClassLoader将图像加载到Applet
设置或获取有关图像图标的信息
方法 目的
void setDescription(String)
String getDescription()
设置或获取图像的描述。此描述供辅助技术使用。
int getIconWidth()
int getIconHeight()
获取图像图标的宽度或高度(以像素为单位)。
观察图像图标的图像加载
方法 目的
void setImageObserver(ImageObserver)
ImageObserver getImageObserver()
设置或获取图像图标的图像观察器。
int getImageLoadStatus() 获取图像图标的图像加载状态。此方法返回的值由MediaTracker定义。

使用图标的示例

下表列出了使用ImageIcon的众多示例之一。

示例 描述位置 备注
LabelDemo 本部分和
如何使用标签
演示如何在应用程序的标签中使用图标,包括有无附加文本的情况。
IconDemo 本部分 使用标签显示大图像;使用同时包含图像和文本的按钮。
CustomIconDemo 本部分 使用由ArrowIcon.java实现的自定义图标类。
TumbleItem 如何创建小程序 一个小程序。在动画中使用图像图标。演示如何调用ImageIconpaintIcon方法。
ButtonDemo 如何使用按钮、复选框和单选按钮 演示如何在应用程序的按钮中使用图标。
CheckBoxDemo 如何使用复选框 使用多个GIF图像。
TabbedPaneDemo 如何使用选项卡窗格 演示如何在选项卡窗格的标签中添加图标。
DialogDemo 如何创建对话框 演示如何在对话框中使用标准图标。
TreeIconDemo 如何使用树 演示如何更改树节点显示的图标。
ActionDemo 如何使用操作 演示如何使用Action在工具栏按钮或菜单项中指定图标。
FileChooserDemo2 如何使用文件选择器 使用一个PNG图像。演示如何在文件选择器中实现图像预览和图像过滤器。

注意: IconDemo中使用的照片版权归©2006年spriggs.net所有,并根据知识共享许可协议进行许可。

上一页: 如何使用模型
下一页: 如何使用边框