Java 中文官方教程 2022 版(十九)(3)https://developer.aliyun.com/article/1486746
使用 getResource 加载图像
大多数情况下,图像图标的数据来自图像文件。您的应用程序的类文件和图像文件可能以多种有效方式配置在文件服务器上。您可能将类文件放在一个 JAR 文件中,或者将图像文件放在一个 JAR 文件中;它们可能在同一个 JAR 文件中,也可能在不同的 JAR 文件中。以下图示说明了这些文件可以配置的几种方式:
如果你正在编写真实世界的应用程序,很可能(也建议)将文件放入包中。有关包的更多信息,请参阅创建和使用包中的学习 Java 语言教程。以下是使用名为"omega"的包的一些可能配置:
所有七种配置都是有效的,相同的代码读取图片:
java.net.URL imageURL = myDemo.class.getResource("images/myImage.gif"); ... if (imageURL != null) { ImageIcon icon = new ImageIcon(imageURL); }
getResource
方法会导致类加载器在程序的类路径中查找目标文件,一旦找到所需文件就会返回一个 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-emulating shell on Microsoft Windows — you must quote the path] 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"?> <!-- JNLP File for DragPictureDemo --> <jnlp spec="1.0+" codebase="https://docs.oracle.com/javase/tutorialJWS/src/uiswing/misc/examples" href="DragPictureDemo.jnlp"> <information> <title>DragPictureDemo</title> <vendor>The Java(tm) Tutorial: Sun Microsystems, Inc.</vendor> <homepage href="https://docs.oracle.com/javase/tutorial/uiswing/misc/examples/index.html#DragPictureDemo"/> <description>DragPictureDemo</description> <description kind="short">A demo showing how to install data transfer on a custom component.</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
查找图像文件。
欲了解更多信息,请参阅以位置无关的方式访问资源和应用程序开发注意事项。
将图像加载到 Applets 中
Applets 通常从提供 applet 的计算机加载图像数据。APPLET
标签是您指定 applet 中使用的图像信息的地方。有关APPLET
标签的更多信息,请参阅使用 APPLET 标签。
在加载图像图标时改善感知性能
由于访问照片图像可能很慢,IconDemoApp.java
使用SwingWorker
来改善用户感知的程序性能。
后台图像加载 — 该程序使用javax.swing.SwingWorker对象在后台线程中加载每张照片图像并计算其缩略图。使用SwingWorker
可以防止程序在加载和缩放图像时出现冻结的情况。
这是处理每个图像的代码:
/** * SwingWorker class that loads the images a background thread and calls publish * when a new one is ready to be displayed. * * We use Void as the first SwingWorker param as we do not need to return * anything from doInBackground(). */ private SwingWorker<Void, ThumbnailAction> loadimages = new SwingWorker<Void, ThumbnailAction>() { /** * Creates full size and thumbnail versions of the target image files. */ @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 { // the image failed to load for some reason // so load a placeholder instead thumbAction = new ThumbnailAction(placeholderIcon, placeholderIcon, imageCaptions[i]); } publish(thumbAction); } // unfortunately we must return something, and only null is valid to // return when the return type is void. return null; } /** * Process all loaded images. */ @Override protected void process(List<ThumbnailAction> chunks) { for (ThumbnailAction thumbAction : chunks) { JButton thumbButton = new JButton(thumbAction); // add the new button BEFORE the last glue // this centers the buttons in the toolbar buttonBar.add(thumbButton, buttonBar.getComponentCount() - 1); } } };
SwingWorker 在后台线程中调用doInBackground
方法。该方法将全尺寸图像、缩略图图像和标题放入ThumbnailAction
对象中。然后,SwingWorker 将ThumbnailAction
传递给process
方法。process
方法在事件分发线程上执行,并通过向工具栏添加按钮来更新 GUI。JButton
有一个接受动作对象的构造函数。动作对象确定按钮的许多属性。在我们的情况下,按钮图标、标题和按下按钮时执行的操作都由ThumbnailAction
确定。
开销 — 该程序最终将所有源图像加载到内存中。这在所有情况下可能并不理想。加载大量非常大的文件可能会导致程序分配大量内存。应注意管理加载的图像数量和大小。
与所有与性能相关的问题一样,这种技术在某些情况下适用,而在其他情况下则不适用。此外,这里描述的技术旨在改善程序的感知性能,但不一定影响其实际性能。
创建自定义图标实现
当createImageIcon
方法无法找到图像时会返回 null,但程序应该怎么做呢?一种可能性是忽略该图像并继续。另一个选择是在无法加载真实图像时提供某种默认图标来显示。再次调用createImageIcon
可能会导致另一个 null,因此使用它不是一个好主意。相反,让我们创建一个自定义Icon
实现。
您可以在MissingIcon.java
中找到自定义图标类的实现。以下是其代码中的有趣部分:
/** * The "missing icon" is a white box with a black border and a red x. * It's used to display something when there are issues loading an * icon from an external location. * * @author 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 的更多信息,请参见执行自定义绘图。
以下代码演示了MissingIcon
类在SwingWorker
的doInBackground
方法中的使用。
private MissingIcon placeholderIcon = new MissingIcon(); ... if(icon != null) { ... } else { // the image failed to load for some reason // so load a placeholder instead thumbAction = new ThumbnailAction(placeholderIcon, placeholderIcon, imageCaptions[i]); }
使用自定义图标会有一些影响:
- 因为图标的外观是动态确定的,图标绘制代码可以使用任何信息 — 例如组件和应用程序状态 — 来确定要绘制什么。
- 根据平台和图像类型,使用自定义图标可能会提高性能,因为绘制简单形状有时比复制图像更快。
- 因为
MissingIcon
不执行任何文件 I/O,所以不需要单独的线程来加载图像。
图像图标 API
以下表格列出了常用的ImageIcon
构造函数和方法。请注意,ImageIcon
不是JComponent
甚至不是Component
的子类。
使用图像图标的 API 分为以下几类:
- 设置、获取和绘制图像图标的图像
- 设置或获取有关图像图标的信息
- 观察图像图标的图像加载状态
设置、获取和绘制图像图标的图像
方法或构造函数 | 目的 |
| ImageIcon() ImageIcon(byte[]) |
ImageIcon(URL, String) | 创建一个ImageIcon
实例,并初始化为包含指定图像。第一个参数指示源——图像、字节数组、文件名或 URL——从中应加载图像图标的图像。源必须是java.awt.Image
类支持的格式:即 GIF、JPEG 或 PNG。第二个参数(如果存在)为图像提供描述。描述也可以通过setDescription
设置,并为辅助技术提供有用的文本信息。 |
void setImage(Image) Image getImage() | 设置或获取图像图标显示的图像。 |
void paintIcon(Component, Graphics, int, int) | 在指定的图形上下文中绘制图像图标的图像。只有在实现执行自己的绘图的自定义图标时才会覆盖此方法。Component 对象用作图像观察者。您可以依赖Component 类提供的默认行为,并传入任何组件。两个int 参数指定绘制图标的左上角。 |
URL getResource(String) in (java.lang.ClassLoader) | 查找具有给定名称的资源。有关更多信息,请参见使用 getResource 加载图像。 |
InputStream getResourceAsStream(String) in (java.lang.ClassLoader) | 查找具有给定名称的资源,并返回用于读取资源的输入流。有关更多信息,请参见将图像加载到小程序中讨论。 |
设置或获取有关图像图标的信息
方法 | 目的 |
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 图像。展示了如何在文件选择器中实现图像预览和图像过滤器。 |
注意: IconDemo
中使用的照片版权归 ©2006 spriggs.net 所有,并在 知识共享许可协议 下授权使用。
如何使用边框
原文:
docs.oracle.com/javase/tutorial/uiswing/components/border.html
每个JComponent
可以有一个或多个边框。边框是非常有用的对象,虽然它们本身不是组件,但知道如何绘制 Swing 组件的边缘。边框不仅用于绘制线条和花哨的边缘,还用于为组件提供标题和周围的空白空间。
注意:
我们的示例在JPanel
、JLabel
和JComponent
的自定义子类上设置了边框。尽管从技术上讲,您可以在任何继承自JComponent
的对象上设置边框,但许多标准 Swing 组件的外观和感觉实现与用户设置的边框不兼容。通常情况下,当您想在除了JPanel
或JLabel
之外的标准 Swing 组件上设置边框时,我们建议将组件放在JPanel
中,并在JPanel
上设置边框。
要在JComponent
周围放置边框,可以使用其setBorder
方法。您可以使用BorderFactory
类来创建 Swing 提供的大多数边框。如果您需要引用边框,比如因为您想在多个组件中使用它,可以将其保存在Border
类型的变量中。以下是创建带边框容器的代码示例:
JPanel pane = new JPanel(); pane.setBorder(BorderFactory.createLineBorder(Color.black));
这是一个包含标签组件的容器的图片。边框绘制的黑线标记了容器的边缘。
本页的其余部分讨论以下主题:
- BorderDemo 示例
- 使用 Swing 提供的边框
- 创建自定义边框
- 边框 API
- 使用边框的示例
BorderDemo 示例
以下图片展示了一个名为BorderDemo
的应用程序,显示了 Swing 提供的边框。稍后我们会展示创建这些边框的代码,在使用 Swing 提供的边框中。
单击启动按钮以使用Java™ Web Start运行 BorderDemo 示例(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
下一张图片展示了一些镜面边框。创建镜面边框时,您需要指定组件顶部、左侧、底部和右侧各占据多少像素。然后,您需要指定镜面边框要绘制的颜色或图标。在选择图标和确定组件大小时需要小心;否则,图标可能会被截断或在组件的角落处不匹配。
下一张图片展示了带标题的边框。使用带标题的边框,您可以将任何边框转换为显示文本描述的边框。如果不指定边框,将使用特定外观的边框。例如,在 Java 外观中,默认的带标题边框使用灰色线条,在 Windows 外观中默认的带标题边框使用浮雕边框。默认情况下,标题横跨边框的左上角,如下图顶部所示。
下一张图片展示了复合边框。使用复合边框,您可以组合任意两个边框,这些边框本身可以是复合边框。
使用 Swing 提供的边框
接下来的代码展示了如何创建和设置前面图中看到的边框。您可以在BorderDemo.java
中找到程序的代码。
//Keep references to the next few borders, //for use in titles and compound borders. Border blackline, raisedetched, loweredetched, raisedbevel, loweredbevel, empty; blackline = BorderFactory.createLineBorder(Color.black); raisedetched = BorderFactory.createEtchedBorder(EtchedBorder.RAISED); loweredetched = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED); raisedbevel = BorderFactory.createRaisedBevelBorder(); loweredbevel = BorderFactory.createLoweredBevelBorder(); empty = BorderFactory.createEmptyBorder(); //Simple borders jComp1.setBorder(blackline); jComp2.setBorder(raisedbevel); jComp3.setBorder(loweredbevel); jComp4.setBorder(empty); //Matte borders ImageIcon icon = createImageIcon("images/wavy.gif", "wavy-line border icon"); //20x22 jComp5.setBorder(BorderFactory.createMatteBorder( -1, -1, -1, -1, icon)); jComp6.setBorder(BorderFactory.createMatteBorder( 1, 5, 1, 1, Color.red)); jComp7.setBorder(BorderFactory.createMatteBorder( 0, 20, 0, 0, icon)); //Titled borders TitledBorder title; title = BorderFactory.createTitledBorder("title"); jComp8.setBorder(title); title = BorderFactory.createTitledBorder( blackline, "title"); title.setTitleJustification(TitledBorder.CENTER); jComp9.setBorder(title); title = BorderFactory.createTitledBorder( loweredetched, "title"); title.setTitleJustification(TitledBorder.RIGHT); jComp10.setBorder(title); title = BorderFactory.createTitledBorder( loweredbevel, "title"); title.setTitlePosition(TitledBorder.ABOVE_TOP); jComp11.setBorder(title); title = BorderFactory.createTitledBorder( empty, "title"); title.setTitlePosition(TitledBorder.BOTTOM); jComp12.setBorder(title); //Compound borders Border compound; Border redline = BorderFactory.createLineBorder(Color.red); //This creates a nice frame. compound = BorderFactory.createCompoundBorder( raisedbevel, loweredbevel); jComp13.setBorder(compound); //Add a red outline to the frame. compound = BorderFactory.createCompoundBorder( redline, compound); jComp14.setBorder(compound); //Add a title to the red-outlined frame. compound = BorderFactory.createTitledBorder( compound, "title", TitledBorder.CENTER, TitledBorder.BELOW_BOTTOM); jComp15.setBorder(compound);
正如您可能注意到的,代码使用BorderFactory
类来创建每个边框。BorderFactory
类位于javax.swing
包中,返回实现Border
接口的对象。
Border
接口及其 Swing 提供的实现位于javax.swing.border
包中。通常情况下,您不需要直接使用边框包中的任何内容,除非在指定特定边框类的常量或引用Border
类型时。
创建自定义边框
如果BorderFactory
不能提供足够的控制来定义边框的形式,那么您可能需要直接使用边框包中的 API — 或者甚至定义自己的边框。除了包含Border
接口外,边框包还包含实现您已经看到的边框的类:LineBorder
、EtchedBorder
、BevelBorder
、EmptyBorder
、MatteBorder
、TitledBorder
和CompoundBorder
。边框包还包含一个名为SoftBevelBorder
的类,它产生类似于BevelBorder
但边缘更柔和的效果。
如果没有适合的 Swing 边框,您可以实现自己的边框。通常,您可以通过创建AbstractBorder
类的子类来实现这一点。在您的子类中,您必须实现至少一个构造函数和以下两个方法:
paintBorder
,其中包含绘制代码,JComponent
执行以绘制边框。getBorderInsets
,指定边框需要绘制自身所需的空间量。
如果自定义边框有插入(通常会有插入),您需要重写AbstractBorder.getBorderInsets(Component c)
和AbstractBorder.getBorderInsets(Component c, Insets insets)
来提供正确的插入。
有关实现边框的示例,请参阅javax.swing.border
包中类的源代码。
边框 API
以下表格列出了常用的边框方法。使用边框的 API 分为两类:
- 使用 BorderFactory 创建边框
- 设置或获取组件的边框
使用 BorderFactory 创建边框
方法 | 目的 |
创建线边框(Color) 创建线边框(Color, int) | 创建一个线边框。第一个参数是一个java.awt.Color 对象,指定线的颜色。可选的第二个参数指定线的宽度(以像素为单位)。 |
| 创建浮雕边框() 创建浮雕边框(Color, Color)
创建浮雕边框(int, Color, Color) | 创建一个浮雕边框。可选的Color
参数指定要使用的高亮和阴影颜色。带有int
参数的方法允许将边框方法指定为EtchedBorder.RAISED
或EtchedBorder.LOWERED
。没有int
参数的方法创建一个降低的浮雕边框。
创建降低斜角边框() | 创建一个边框,使组件看起来比周围区域更低。 |
创建凸起斜角边框() | 创建一个边框,使组件看起来比周围区域更高。 |
创建斜角边框(int, Color, Color, Color, Color) | 创建一个凸起或凹陷的斜角边框,指定要使用的颜色。整数参数可以是BevelBorder.RAISED
或BevelBorder.LOWERED
。使用三个参数的构造函数,您可以指定高亮和阴影颜色。使用五个参数的构造函数,您按顺序指定外部高亮,内部高亮,外部阴影和内部阴影颜色。
Border createEmptyBorder() Border createEmptyBorder(int, int, int, int) | 创建一个不可见的边框。如果不指定参数,则边框不占用空间,这在创建没有可见边界的标题边框时很有用。可选参数指定边框在组件顶部、左侧、底部和右侧(按顺序)占用的像素数。此方法可用于在组件周围放置空白空间。 |
MatteBorder createMatteBorder(int, int, int, int, Color) MatteBorder createMatteBorder(int, int, int, int, Icon) | 创建一个亚光边框。整数参数指定边框在组件顶部、左侧、底部和右侧(按顺序)占用的像素数。颜色参数指定边框应填充其区域的颜色。图标参数指定边框应平铺其区域的图标。 |
| TitledBorder createTitledBorder(String) TitledBorder createTitledBorder(Border)
TitledBorder createTitledBorder(Border, String)
TitledBorder createTitledBorder(Border, String, int, int)
TitledBorder createTitledBorder(Border, String, int, int, Font)
TitledBorder createTitledBorder(Border, String, int, int, Font, Color) | 创建一个带标题的边框。字符串参数指定要显示的标题。可选的字体和颜色参数指定标题文本要使用的字体和颜色。边框参数指定应显示的边框以及标题。如果未指定边框,则使用特定于外观的默认边框。默认情况下,标题跨越其伴侣边框的顶部,并且左对齐。可选的整数参数按顺序指定标题的位置和对齐方式。TitledBorder
定义了这些可能的位置:ABOVE_TOP
,TOP
(默认),BELOW_TOP
,ABOVE_BOTTOM
,BOTTOM
和BELOW_BOTTOM
。您可以将对齐方式指定为LEADING
(默认),CENTER
或TRAILING
。在具有西方字母表的语言环境中,LEADING
等同于LEFT
,TRAILING
等同于RIGHT
。 |
CompoundBorder createCompoundBorder(Border, Border) | 将两个边框合并为一个。第一个参数指定外边框;第二个参数指定内边框。 |
设置或获取组件的边框
方法 | 目的 |
void setBorder(Border) Border getBorder() | 设置或获取接收JComponent 的边框。 |
| void setBorderPainted(boolean) boolean isBorderPainted()
(在AbstractButton
,JMenuBar
,JPopupMenu
,JProgressBar
和JToolBar
中) | 设置或获取组件的边框是否应显示。 |
使用边框的示例
本课程中的许多示例使用边框。以下表格列出了一些有趣的案例。
示例 | 描述位置 | 备注 |
BorderDemo |
本节 | 展示BorderFactory 可以创建的每种边框类型的示例。还使用空边框在每个窗格及其内容之间添加间距。 |
BoxAlignmentDemo |
如何使用 BoxLayout | 使用带标题的边框。 |
BoxLayoutDemo |
如何使用 BoxLayout | 使用红线显示容器的边缘位置,以便您可以看到 BoxLayout 中额外空间是如何分配的。 |
ComboBoxDemo2 |
如何使用组合框 | 使用复合边框将线边框与空边框结合在一起。空边框在线和组件内部之间提供空间。 |