这些Java教程是针对JDK 8编写的。本页面中描述的示例和实践不利用后续版本中引入的改进,并可能使用不再可用的技术。
请参阅Java语言更改,了解Java SE 9及后续版本中更新的语言功能的摘要。
请参阅JDK发布说明,了解所有JDK发布的新功能、增强功能以及已删除或弃用选项的信息。
许多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
参数是一个字符串,允许辅助技术帮助视力受损的用户理解图标传达的信息。
通常,应用程序会提供自己的一组用作应用程序一部分的图像,就像我们的许多演示中使用的图像一样。您应该使用Class
的getResource
方法来获取图像的路径。这样应用程序可以验证图像是否可用,并在图像不可用时提供合理的错误处理。当图像不是应用程序的一部分时,不应使用getResource
,而应直接使用ImageIcon
构造函数。例如:
ImageIcon icon = new ImageIcon("images/middle.gif", "一个漂亮但没有意义的斑点");
当您在ImageIcon
构造函数中指定文件名或URL时,会阻塞处理直到图像数据完全加载或数据位置被证明无效。如果数据位置无效(但非空),仍然会成功创建ImageIcon
,但它没有大小,因此不会绘制任何内容。如createImageIcon
方法所示,建议在将URL传递给ImageIcon
构造函数之前,首先验证URL是否指向现有文件。这样可以在文件不存在时进行优雅的错误处理。如果在加载图像时需要更多信息,可以通过调用setImageObserver
方法在图像图标上注册一个观察者。
在内部,每个图像图标使用一个Image
对象来保存图像数据。
本节的其余部分涵盖以下主题:
这是一个使用六个图像图标的应用程序。其中五个显示缩略图像,第六个显示全尺寸照片。
点击“启动”按钮以使用Java™ Web Start运行IconDemo(下载JDK 7或更高版本)。或者,如果要编译和运行示例,请参考示例索引。
点击任何缩略图像以查看全尺寸照片。
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()); } }
通常,图像图标的数据来自图像文件。您的应用程序的类和图像文件可能在文件服务器上以多种方式配置。您可能将类文件和图像文件放在一个JAR文件中,或者将它们放在不同的JAR文件中。以下图示说明了这些文件可能配置的几种方式:
类文件与包含图像文件(PNG格式)的图像目录相邻。 | 类文件与JAR文件在同一目录下。JAR文件包含一个名为images 的目录,其中包含所有图像文件。 |
类文件在一个JAR文件中,图像文件在另一个JAR文件中。 | 类文件和图像文件在同一个JAR文件中。 |
如果你正在编写一个真实的应用程序,很可能(也推荐)将你的文件放入一个包中。关于包的更多信息,请参阅创建和使用包 在 学习Java语言 章节。这里有一些可能的配置,使用一个名为"omega"的包:
类文件在名为omega 的目录中。图像在omega/images 目录中。 |
类文件在omega 目录中。图像在JAR文件中,不在omega 目录内,但是使用omega/images 层次结构创建。 |
一个包含类文件在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
文件,类加载器将返回第一个包含该文件的实例。
以下是三种指定类路径的方式:
使用-cp
或-classpath
命令行参数。例如,假设图像在名为images.jar
的JAR文件中,类文件在当前目录中:
java -cp .;images.jar MyDemo [Microsoft Windows] java -cp ".;images.jar" MyDemo [UNIX模拟Microsoft Windows上的shell - 必须引用路径] java -cp .:images.jar MyDemo [UNIX]
如果图像和类文件在不同的JAR文件中,命令行将类似于:
java -cp .;MyDemo.jar;images.jar MyDemo [Microsoft Windows]
如果所有文件都在一个JAR文件中,可以使用以下任何一个命令:
java -jar MyAppPlusImages.jar java -cp .;MyAppPlusImages.jar MyApp [Microsoft Windows]
更多信息,请参阅JAR文件教程。
在程序的JNLP文件中(由Java Web Start使用)。例如,这是DragPictureDemo
使用的JNLP文件:
<?xml version="1.0" encoding="utf-8"?> <!-- DragPictureDemo的JNLP文件 --> <jnlp spec="1.0+" codebase="https://docs.oracle.com/javase/tutorialJWS/src/uiswing/misc/examples" href="DragPictureDemo.jnlp"> <information> <title>DragPictureDemo</title> <vendor>Java(tm)教程:Sun Microsystems, Inc.</vendor> <homepage href="https://docs.oracle.com/javase/tutorial/uiswing/misc/examples/index.html#DragPictureDemo"/> <description>DragPictureDemo</description> <description kind="short">演示如何在自定义组件上安装数据传输</description> <offline-allowed/> </information> <resources> <j2se version="1.6+"/> <jar href="allClasses.jar"/> <jar href="images.jar"/> </resources> <application-desc main-class="DragPictureDemo"/> </jnlp>
在此示例中,类文件和图像文件位于不同的JAR文件中。使用XML的jar
标签指定JAR文件。
设置CLASSPATH
环境变量。这种方法不推荐。如果未设置CLASSPATH
,则默认使用当前目录("."),然后是JRE附带的系统类的位置。
大多数Swing教程示例将图像放在包含示例类文件的目录下的images
目录中。当我们为示例创建JAR文件时,保持相同的相对位置,尽管通常我们将类文件和图像JAR文件放在不同的JAR文件中。无论类和图像文件在文件系统中的位置如何 - 在一个JAR文件中还是在多个JAR文件中,在命名包中还是在默认包中 - 相同的代码都使用getResource
来找到图像文件。
有关更多信息,请参见以位置无关方式访问资源和应用程序开发注意事项。
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.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的更多信息,请参见执行自定义绘制。
以下代码演示了在SwingWorker
的doInBackground
方法中使用MissingIcon
类的示例。
private MissingIcon placeholderIcon = new MissingIcon(); ... if(icon != null) { ... } else { // 图片由于某种原因加载失败 // 所以加载一个占位符 thumbAction = new ThumbnailAction(placeholderIcon, placeholderIcon, imageCaptions[i]); }
使用自定义图标有一些影响:
因为图标的外观是动态确定的,图标绘制代码可以使用任何信息(例如组件和应用程序状态)来确定要绘制的内容。
MissingIcon
不执行任何文件I/O操作,因此不需要单独的线程来加载图像。
以下表格列出了常用的ImageIcon
构造函数和方法。请注意,ImageIcon
不是JComponent
甚至不是Component
的子类。
使用图像图标的API可分为以下类别:
设置、获取和绘制图像图标的图像ImageIcon()ImageIcon
java.awt.Image
setDescription
辅助技术void setImage(Image)
Component
Component
int
URL getResource(String)
方法 | 目的 |
---|---|
void setDescription(String) String getDescription() |
设置或获取图像的描述。此描述供辅助技术使用。 |
int getIconWidth() int getIconHeight() |
获取图像图标的宽度或高度(以像素为单位)。 |
方法 | 目的 |
---|---|
void setImageObserver(ImageObserver) ImageObserver getImageObserver() |
设置或获取图像图标的图像观察器。 |
int getImageLoadStatus() | 获取图像图标的图像加载状态。此方法返回的值由MediaTracker 定义。 |
下表列出了使用ImageIcon
的众多示例之一。
示例 | 描述位置 | 备注 |
---|---|---|
LabelDemo |
本部分和 如何使用标签 |
演示如何在应用程序的标签中使用图标,包括有无附加文本的情况。 |
IconDemo |
本部分 | 使用标签显示大图像;使用同时包含图像和文本的按钮。 |
CustomIconDemo |
本部分 | 使用由ArrowIcon.java 实现的自定义图标类。 |
TumbleItem |
如何创建小程序 | 一个小程序。在动画中使用图像图标。演示如何调用ImageIcon 的paintIcon 方法。 |
ButtonDemo |
如何使用按钮、复选框和单选按钮 | 演示如何在应用程序的按钮中使用图标。 |
CheckBoxDemo |
如何使用复选框 | 使用多个GIF图像。 |
TabbedPaneDemo |
如何使用选项卡窗格 | 演示如何在选项卡窗格的标签中添加图标。 |
DialogDemo |
如何创建对话框 | 演示如何在对话框中使用标准图标。 |
TreeIconDemo |
如何使用树 | 演示如何更改树节点显示的图标。 |
ActionDemo |
如何使用操作 | 演示如何使用Action 在工具栏按钮或菜单项中指定图标。 |
FileChooserDemo2 |
如何使用文件选择器 | 使用一个PNG 图像。演示如何在文件选择器中实现图像预览和图像过滤器。 |