Java 中文官方教程 2022 版(十九)
如何使用文本区域
原文:
docs.oracle.com/javase/tutorial/uiswing/components/textarea.html
JTextArea类提供了一个显示多行文本并允许用户编辑文本的组件。如果您只需要从用户那里获取一行输入,应该使用文本字段。如果您希望文本区域使用多种字体或其他样式显示其文本,应该使用编辑器窗格或文本窗格。如果显示的文本长度有限且用户永远不会编辑它,请使用标签。
教程的许多示例使用不可编辑的文本区域来显示程序输出。这里是一个名为TextDemo的示例的图片,它允许您使用文本字段(顶部)输入文本,然后将输入的文本附加到文本区域(底部)。

点击启动按钮以使用Java™ Web Start运行 TextDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
您可以在TextDemo.java中找到此程序的完整代码。以下代码创建并初始化了文本区域:
textArea = new JTextArea(5, 20);
JScrollPane scrollPane = new JScrollPane(textArea);
textArea.setEditable(false);
JTextArea构造函数的两个参数分别是文本区域应显示的行数和列数的提示。包含文本区域的滚动窗格在确定滚动窗格应有多大时会注意这些提示。
如果没有创建滚动窗格,文本区域将不会自动滚动。前面代码片段中显示的JScrollPane构造函数设置了文本区域在滚动窗格中的显示,并指定了在需要时滚动窗格的滚动条应该可见。如果需要更多信息,请参阅如何使用滚动窗格。
文本区域默认可编辑。代码setEditable(false)使文本区域不可编辑。它仍然可选择,用户可以从中复制数据,但用户不能直接更改文本区域的内容。
以下代码向文本区域添加文本。请注意,文本系统在内部使用'\n'字符表示换行;有关详细信息,请参阅DefaultEditorKit的 API 文档。
private final static String newline = "\n";
...
textArea.append(text + newline);
除非用户通过在文本区域中点击或拖动来移动插入点,否则文本区域会自动滚动,以便显示追加的文本。您可以通过将插入点移动到文本区域末尾来强制文本区域滚动到底部,方法是在调用append之后将插入点移动到文本区域末尾:
textArea.setCaretPosition(textArea.getDocument().getLength());
自定义文本区域
您可以通过多种方式自定义文本区域。例如,尽管给定的文本区域只能显示一种字体和颜色的文本,但您可以设置它使用的字体和颜色。此自定义选项可在任何组件上执行。您还可以确定文本区域如何换行以及每个制表符的字符数。最后,您可以使用JTextArea类从JTextComponent类继承的方法来设置属性,如插入点、拖动支持或颜色选择。
下面的代码取自TextSamplerDemo.java,演示了如何初始化一个可编辑文本区域。文本区域使用指定的斜体字体,并在单词之间换行。
JTextArea textArea = new JTextArea(
"This is an editable JTextArea. " +
"A text area is a \"plain\" text component, " +
"which means that although it can display text " +
"in any font, all of the text is in the same font."
);
textArea.setFont(new Font("Serif", Font.ITALIC, 16));
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
默认情况下,文本区域不会自动换行显示过长的行。相反,它会将所有文本行放在一个行中,直到遇到换行符,并且如果文本区域位于滚动窗格中,则允许水平滚动。此示例通过调用setLineWrap方法打开了换行显示,并调用setWrapStyleWord方法指示文本区域应在单词边界而不是字符边界处换行。
为了提供滚动功能,示例将文本区域放在了一个滚动窗格中。
JScrollPane areaScrollPane = new JScrollPane(textArea);
areaScrollPane.setVerticalScrollBarPolicy(
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
areaScrollPane.setPreferredSize(new Dimension(250, 250));
您可能已经注意到,在此示例中使用的JTextArea构造函数没有指定行数或列数。相反,代码通过设置滚动窗格的首选大小来限制文本区域的大小。
另一个示例:TextAreaDemo
TextAreaDemo示例介绍了一个带有特殊功能的可编辑文本区域——单词补全功能。当用户输入单词时,如果程序的词汇表中包含以已输入内容开头的单词,程序会提供补全提示。下面是TextAreaDemo应用程序的图片。

点击“启动”按钮以使用Java™ Web Start运行 TextAreaDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
您可以在TextAreaDemo.java中找到此程序的完整代码。
此示例为文本区域提供了默认滚动条策略的滚动功能。默认情况下,只有在显示区域完全填满文本且没有空间附加新单词时,垂直滚动条才会出现。您可以使用以下代码提供此类型的滚动窗格:
textArea.setWrapStyleWord(true);
jScrollPane1 = new JScrollPane(textArea);
如上所述,文本区域是可编辑的。您可以通过键入和粘贴文本,或删除部分文本或整个内容来操作文本区域。还可以尝试使用文本区域内的标准键绑定来编辑文本。
现在探索单词完成功能是如何实现的。输入类似"Swing"或"special"的单词。一旦输入"sw",程序会显示一个可能的完成"ing",用浅蓝色突出显示。按 Enter 接受完成或继续输入。
以下代码向文本区域的文档添加了文档监听器:
textArea.getDocument().addDocumentListener(this);
当您开始输入单词时,insertUpdate 方法会检查程序的词汇表是否包含输入的前缀。一旦找到前缀的完成,对invokeLater方法的调用会提交一个任务以稍后更改文档。重要的是要记住,您不能在文档事件通知中修改文档,否则会出现异常。请查看下面的代码。
String prefix = content.substring(w + 1).toLowerCase();
int n = Collections.binarySearch(words, prefix);
if (n < 0 && -n <= words.size()) {
String match = words.get(-n - 1);
if (match.startsWith(prefix)) {
// A completion is found
String completion = match.substring(pos - w);
// We cannot modify Document from within notification,
// so we submit a task that does the change later
SwingUtilities.invokeLater(
new CompletionTask(completion, pos + 1));
}
} else {
// Nothing found
mode = Mode.INSERT;
}
粗体显示的代码演示了如何创建选择。光标首先设置在完整单词的末尾,然后移回到最后一个字符输入后的位置。moveCaretPosition 方法不仅将光标移动到新位置,还选择两个位置之间的文本。完成任务的代码如下所示:
private class CompletionTask implements Runnable {
String completion;
int position;
CompletionTask(String completion, int position) {
this.completion = completion;
this.position = position;
}
public void run() {
textArea.insert(completion, position);
textArea.setCaretPosition(position + completion.length());
textArea.moveCaretPosition(position);
mode = Mode.COMPLETION;
}
}
文本区域 API
以下表格列出了常用的JTextArea构造函数和方法。您可能调用的其他方法在JTextComponent中定义,并在文本组件 API 中列出。
您还可以调用文本区域从其其他祖先继承的方法,例如setPreferredSize、setForeground、setBackground、setFont等。请参阅 The JComponent Class 以查看常用继承方法的表格。
使用文本区域的 API 包括以下类别:
-
设置或获取内容
-
微调外观
-
实现功能
设置或获取内容
| 方法或构造函数 | 目的 |
|---|
| JTextArea() JTextArea(String)
JTextArea(int, int) | 创建文本区域。当存在时,String参数包含初始文本。int参数分别指定所需的列宽和行高。
| void setText(String) String getText()
(定义在JTextComponent中) | 设置或获取文本区域显示的文本。
调整文本区域的外观
| 方法 | 目的 |
|---|
| void setEditable(boolean) boolean isEditable()
(定义在JTextComponent中) | 设置或指示用户是否可以编辑文本区域中的文本。
| void setColumns(int); int getColumns() | 设置或获取文本区域显示的列数。这实际上只是计算区域首选宽度的提示。 |
|---|---|
| void setRows(int); int getRows() | 设置或获取文本区域显示的行数。这是计算区域首选高度的提示。 |
| int setTabSize(int) | 设置制表符相当于的字符数。 |
| int setLineWrap(boolean) | 设置是否在行过长无法适应分配的宽度时换行。默认情况下,此属性为 false,行不会换行。 |
| int setWrapStyleWord(boolean) | 设置行是否可以在空格(单词边界)或任何字符处换行。默认情况下,此属性为 false,行可以在任何字符处换行(如果打开了换行)。 |
实现文本区域的功能
| 方法 | 目的 |
|---|---|
void selectAll() (定义在JTextComponent中) |
选择文本区域中的所有字符。 |
| void append(String) | 将指定的文本添加到文本区域的末尾。 |
| void insert(String, int) | 在指定位置插入指定的文本。 |
| void replaceRange(String, int, int) | 用指定的字符串替换指定位置之间的文本。 |
| int getLineCount() int getLineOfOffset(int)
int getLineEndOffset(int) | 用于查找行号或指定行的开头或结尾位置的实用程序。 |
使用文本区域的示例
这个表格列出了使用文本区域的示例,并指向这些示例的描述位置。
| 示例 | 描述位置 | 注释 |
|---|---|---|
| 文本演示 | 本节 | 一个将用户输入的文本追加到文本区域的应用程序。 |
| 文本区域演示 | 本节 | 一个带有单词补全功能的文本区域的应用程序。 |
| 文本示例演示 | 使用文本组件 | 使用每个 Swing 文本组件中的一个。 |
| HTML 演示 | 如何在 Swing 组件中使用 HTML | 一个文本区域,允许用户输入 HTML 代码以在标签中显示。 |
| 基本拖放 | 拖放简介 | 演示了几个 Swing 组件的内置拖放功能,包括文本区域。 |
| 焦点概念演示 | 如何使用焦点子系统 | 演示了焦点如何使用几个包含文本区域的组件。 |
如何使用文本字段
原文:
docs.oracle.com/javase/tutorial/uiswing/components/textfield.html
文本字段是一种基本的文本控件,允许用户输入少量文本。当用户指示输入文本完成(通常通过按 Enter 键),文本字段会触发一个 action event。如果您需要从用户获取多行输入,请使用文本区域。
Swing API 提供了几个类,用于包含文本字段的各种文本字段或包含文本字段的组件。
JTextField |
本节内容涵盖:基本文本字段。 |
|---|---|
JFormattedTextField |
允许您指定用户可以输入的合法字符集的JTextField子类。参见如何使用格式化文本字段。 |
JPasswordField |
不显示用户输入的字符的JTextField子类。参见如何使用密码字段。 |
JComboBox |
可编辑,并提供一个字符串菜单供选择。参见如何使用组合框。 |
JSpinner |
将格式化文本字段与一对小按钮结合起来,使用户可以选择上一个或下一个可用值。参见如何使用微调器。 |
以下示例显示了一个基本文本字段和一个文本区域。文本字段可编辑,而文本区域不可编辑。当用户在文本字段中按 Enter 键时,程序将文本字段的内容复制到文本区域,并选择文本字段中的所有文本。

点击“启动”按钮以使用Java™ Web Start运行 TextDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
你可以在TextDemo.java中找到此程序的完整代码。以下代码创建并设置文本字段:
textField = new JTextField(20);
传递给 JTextField 构造函数的整数参数,在示例中为 20,表示字段中的列数。此数字与字段当前字体提供的度量一起用于计算字段的首选宽度。它不限制用户可以输入的字符数。要实现这一点,你可以使用格式化文本字段或文档监听器,如文本组件功能中所述。
注意:
我们鼓励您为每个文本字段指定列数。如果不指定列数或首选大小,则字段的首选大小会随文本更改而更改,这可能导致不必要的布局更新。
下一行代码将一个 TextDemo 对象注册为文本字段的动作监听器。
textField.addActionListener(this);
actionPerformed 方法处理文本字段的动作事件:
private final static String newline = "\n";
...
public void actionPerformed(ActionEvent evt) {
String text = textField.getText();
textArea.append(text + newline);
textField.selectAll();
}
注意使用 JTextField 的 getText 方法来检索当前文本字段中包含的文本。此方法返回的文本不包括触发动作事件的 Enter 键的换行符。
你已经看到了如何使用基本文本字段。因为 JTextField 类继承自 JTextComponent 类,文本字段非常灵活,几乎可以按照你喜欢的任何方式进行自定义。例如,你可以添加文档监听器或文档过滤器以在文本更改时收到通知,并在过滤器情况下相应地修改文本字段。有关文本组件的信息可以在文本组件功能中找到。但在自定义 JTextField 之前,请确保其他基于文本字段的组件中的一个不适合你的需求。
通常文本字段与描述文本字段的标签配对使用。请参阅使用文本字段的示例以获取有关创建这些配对的指导。
另一个示例:TextFieldDemo
TextFieldDemo 示例介绍了一个文本字段和一个文本区域。你可以在TextFieldDemo.java中找到此程序的完整代码。
当你在文本字段中输入字符时,程序会在文本区域中搜索输入的文本。如果找到该条目,则会进行高亮显示。如果程序未能找到该条目,则文本字段的背景变为粉色。文本区域下方的状态栏显示文本是否被找到。按下 Escape 键可开始新的搜索或完成当前搜索。这是 TextFieldDemo 应用程序的图片。

单击“启动”按钮以使用Java™ Web Start运行 TextFieldDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
为了突出显示文本,此示例使用了一个高亮器和一个绘制器。下面的代码创建并设置了文本区域的高亮器和绘制器。
final Highlighter hilit;
final Highlighter.HighlightPainter painter;
...
hilit = new DefaultHighlighter();
painter = new DefaultHighlighter.DefaultHighlightPainter(HILIT_COLOR);
textArea.setHighlighter(hilit);
该代码向文本字段的文档添加了一个文档监听器。
entry.getDocument().addDocumentListener(this);
文档监听器的 insertUpdate 和 removeUpdate 方法调用 search 方法,该方法不仅在文本区域中执行搜索,还处理高亮显示。以下代码突出显示找到的文本,将插入符设置为找到的匹配项的末尾,为文本字段设置默认背景,并在状态栏中显示消息。
hilit.addHighlight(index, end, painter);
textArea.setCaretPosition(end);
entry.setBackground(entryBg);
message("'" + s + "' found. Press ESC to end search");
状态栏是一个 JLabel 对象。下面的代码展示了 message 方法的实现方式。
private JLabel status;
...
void message(String msg) {
status.setText(msg);
}
如果文本区域中没有匹配项,以下代码将更改文本字段的背景颜色为粉红色,并显示适当的信息消息。
entry.setBackground(ERROR_COLOR);
message("'" + s + "' not found. Press ESC to start a new search");
CancelAction 类负责处理 Escape 键,如下所示。
class CancelAction extends AbstractAction {
public void actionPerformed(ActionEvent ev) {
hilit.removeAllHighlights();
entry.setText("");
entry.setBackground(entryBg);
}
}
文本字段 API
以下表格列出了常用的 JTextField 构造函数和方法。您可能会调用的其他方法在 JTextComponent 类中定义。请参阅 The Text Component API。
您还可以调用从文本字段的其他祖先继承的方法,例如 setPreferredSize、setForeground、setBackground、setFont 等。请参阅 The JComponent Class 以查看常用继承方法的表格。
使用文本字段的 API 分为以下几类:
-
设置或获取字段的内容
-
微调字段的外观
-
实现字段的功能
设置或获取字段的内容
| 方法或构造函数 | 目的 |
|---|
| JTextField() JTextField(String)
JTextField(int) | 创建一个文本字段。当存在时,int 参数指定了所需的列宽。String 参数包含字段的初始文本。 |
| void setText(String) String getText() | 设置或获取文本字段显示的文本。 |
(定义在JTextComponent中) | 设置或获取文本字段显示的文本。 |
调整字段外观
| 方法 | 目的 |
|---|
| void setEditable(boolean) boolean isEditable() | 设置或指示用户是否可以编辑文本字段中的文本。 |
(定义在JTextComponent中) | 设置或指示用户是否可以编辑文本字段中的文本。 |
| void setColumns(int); int getColumns() | 设置或获取文本字段显示的列数。这实际上只是计算字段首选宽度的提示。 |
|---|---|
| void setHorizontalAlignment(int); int getHorizontalAlignment() | 设置或获取文本在其区域内水平对齐的方式。您可以使用JTextField.LEADING、JTextField.CENTER和JTextField.TRAILING作为参数。 |
实现字段功能
| 方法 | 目的 |
|---|---|
| void addActionListener(ActionListener) void removeActionListener(ActionListener) | 添加或移除一个动作监听器。 |
void selectAll() (定义在JTextComponent中) |
选择文本字段中的所有字符。 |
使用文本字段的示例
本表显示了一些使用文本字段的示例,并指向这些示例的描述位置。有关所有文本字段类型中类似的代码示例,例如处理布局,请查看相关组件的示例列表,如格式化文本字段和微调器。
| 示例 | 描述位置 | 注释 |
|---|---|---|
| 文本演示 | 本节 | 使用基本文本字段和动作监听器的应用程序。 |
| 文本字段演示 | 本节 | 使用文本字段和文本区域的应用程序。在文本区域中搜索以从文本字段中找到条目。 |
| 对话框演示 | 如何制作对话框 | CustomDialog.java 包含一个检查数值的文本字段。您可以通过点击“更多对话框”选项卡,选择“输入验证对话框”选项,然后点击“显示!”按钮来弹出对话框。 |
| 文本示例演示 | 使用文本组件 | 使用GridBagLayout和一个便利方法来布局标签-文本字段对:
addLabelTextRows(JLabel[] *labels*,
JTextField[] *textFields*,
GridBagLayout *gridbag*,
Container *container*)
|
| 文本输入演示 | 如何使用格式化文本字段 | 使用SpringLayout和SpringUtilities便利方法来布局标签-文本字段对:
makeCompactGrid(Container *parent*,
int *rows*, int *cols*,
int *initialX*, int *initialY*,
int *xPad*, int *yPad*)
|
如果您在 JavaFX 中编程,请参阅文本字段。
如何使用工具栏
原文:
docs.oracle.com/javase/tutorial/uiswing/components/toolbar.html
JToolBar是一个容器,将多个组件(通常是带有图标的按钮)分组为一行或一列。通常,工具栏提供了访问菜单中同样功能的便捷方式。如何使用操作描述了如何在菜单项和工具栏按钮中提供相同的功能。
以下图片显示了一个名为ToolBarDemo的应用程序,其中包含一个位于文本区域上方的工具栏。点击“启动”按钮以使用Java™ Web Start运行 ToolBarDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行,请参考示例索引。

默认情况下,用户可以将工具栏拖动到容器的另一边缘或拖出到自己的窗口。下图显示了用户将工具栏拖动到容器右边缘后应用程序的外观。

要使拖动行为正常工作,工具栏必须位于使用BorderLayout布局管理器的容器中。工具栏影响的组件通常位于容器的中心。工具栏必须是容器中唯一的其他组件,并且不能位于中心。
下图显示了用户将工具栏拖出窗口后应用程序的外观。

以下代码创建了工具栏并将其添加到容器中。您可以在ToolBarDemo.java中找到整个程序。
public class ToolBarDemo extends JPanel
implements ActionListener {
...
public ToolBarDemo() {
super(new BorderLayout());
...
JToolBar toolBar = new JToolBar("Still draggable");
addButtons(toolBar);
...
setPreferredSize(new Dimension(450, 130));
add(toolBar, BorderLayout.PAGE_START);
add(scrollPane, BorderLayout.CENTER);
}
...
}
此代码通过将两个组件放置在由边界布局控制的面板中,将工具栏放置在PAGE_START位置,滚动窗格放置在CENTER位置,将工具栏定位在滚动窗格上方。由于滚动窗格位于中心,容器中除了工具栏之外没有其他组件,默认情况下工具栏可以被拖动到容器的其他边缘。工具栏也可以被拖动到自己的窗口中,此时窗口的标题为“仍可拖动”,由JToolBar构造函数指定。
创建工具栏按钮
工具栏中的按钮是普通的JButton实例,使用了来自 Java 外观和感觉图形存储库的图像。如果您的工具栏具有 Java 外观和感觉,请使用Java 外观和感觉图形存储库中的图像。
这是创建按钮并将其添加到工具栏的代码。
protected void addButtons(JToolBar toolBar) {
JButton button = null;
//first button
button = makeNavigationButton("Back24", PREVIOUS,
"Back to previous something-or-other",
"Previous");
toolBar.add(button);
//second button
button = makeNavigationButton("Up24", UP,
"Up to something-or-other",
"Up");
toolBar.add(button);
*...//similar code for creating and adding the third button...*
}
protected JButton makeNavigationButton(String imageName,
String actionCommand,
String toolTipText,
String altText) {
//Look for the image.
String imgLocation = "images/"
+ imageName
+ ".gif";
URL imageURL = ToolBarDemo.class.getResource(imgLocation);
//Create and initialize the button.
JButton button = new JButton();
button.setActionCommand(actionCommand);
button.setToolTipText(toolTipText);
button.addActionListener(this);
if (imageURL != null) { //image found
button.setIcon(new ImageIcon(imageURL, altText));
} else { //no image found
button.setText(altText);
System.err.println("Resource not found: " + imgLocation);
}
return button;
}
第一次调用makeNavigationButton创建了第一个按钮的图像,使用了图形存储库中的 24x24“返回”导航图像。
除了找到按钮的图像之外,makeNavigationButton方法还创建按钮,设置其动作命令和工具提示文本的字符串,并为按钮添加动作监听器。如果图像丢失,该方法会打印错误消息并向按钮添加文本,以便按钮仍然可用。
注意:
如果工具栏中的任何按钮重复了其他组件(如菜单项)的功能,您可能应该按照如何使用操作中描述的方式创建并添加工具栏按钮。
自定义工具栏
通过向上述示例添加几行代码,我们可以展示一些更多的工具栏功能:
-
使用
setFloatable(false)使工具栏无法移动。 -
使用
setRollover(true)在用户用光标悬停在工具栏按钮上时视觉指示。 -
向工具栏添加分隔符。
-
向工具栏添加非按钮组件。
您可以通过运行 ToolBarDemo2 来查看这些功能。单击“启动”按钮以使用Java™ Web Start运行 ToolBarDemo2(下载 JDK 7 或更高版本)。或者,要自行编译和运行,请参考示例索引。
您可以在ToolBarDemo2.java中找到此程序的完整代码。下面您可以看到使用这些自定义功能的新 UI 的图片。

因为工具栏不再可以被拖动,所以它的左边缘不再有凸起。以下是关闭拖动的代码:
toolBar.setFloatable(false);
工具栏处于悬停模式,因此光标下的按钮有视觉指示。视觉指示的类型取决于外观和感觉。例如,Metal 外观和感觉使用渐变效果来指示光标下的按钮,而其他类型的外观和感觉使用边框来实现此目的。以下是设置悬停模式的代码:
toolBar.setRollover(true);
上面示例中的另一个明显区别是工具栏包含两个新组件,这些组件之前有一个称为分隔符的空格。这是添加分隔符的代码:
toolBar.addSeparator();
这是添加新组件的代码:
//fourth button
button = new JButton("Another button");
...
toolBar.add(button);
//fifth component is NOT a button!
JTextField textField = new JTextField("A text field");
...
toolBar.add(textField);
通过调用setAlignmentY方法,您可以轻松地使工具栏组件顶部对齐或底部对齐,而不是居中。例如,要使工具栏中所有组件的顶部对齐,请在每个组件上调用setAlignmentY(TOP_ALIGNMENT)。类似地,您可以使用setAlignmentX方法在工具栏垂直时指定组件的对齐方式。这种布局灵活性是可能的,因为工具栏使用BoxLayout来定位它们的组件。有关更多信息,请参阅如何使用 BoxLayout。
工具栏 API
以下表格列出了常用的JToolBar构造函数和方法。您可能调用的其他方法在 The JComponent Class 中的 API 表中列出。
| 方法或构造函数 | 目的 |
|---|
JToolBar(String, int) | 创建一个工具栏。可选的 int 参数允许您指定方向;默认为HORIZONTAL。可选的String参数允许您指定工具栏窗口的标题,如果它被拖到容器外部。|
| Component add(Component) | 将组件添加到工具栏。您可以使用AbstractButton定义的setAction(Action)方法将按钮与Action关联起来。 |
|---|---|
| void addSeparator() | 在工具栏末尾添加一个分隔符。 |
| void setFloatable(boolean) boolean isFloatable() | 浮动属性默认为 true,表示用户可以将工具栏拖出到单独的窗口中。要禁用工具栏拖动,请使用toolBar.setFloatable(false)。某些外观和感觉类型可能会忽略此属性。 |
| void setRollover(boolean) boolean isRollover() | 默认情况下,rollover 属性为 false。要使工具栏按钮在用户用光标悬停在其上时以视觉方式指示,将此属性设置为 true。某些外观可能会忽略此属性。 |
使用工具栏的示例
此表列出使用 JToolBar 的示例,并指向这些示例所描述的位置。
| 示例 | 描述位置 | 备注 |
|---|---|---|
ToolBarDemo |
本页 | 一个只有图标按钮的基本工具栏。 |
ToolBarDemo2 |
本页 | 演示了一个非浮动工具栏处于 rollover 模式,其中包含一个分隔符和一个非按钮组件。 |
ActionDemo |
如何使用 Actions | 使用 Action 对象实现工具栏。 |
如何使用工具提示
原文:
docs.oracle.com/javase/tutorial/uiswing/components/tooltip.html
为任何JComponent对象创建工具提示很容易。使用setToolTipText方法为组件设置工具提示。例如,要向三个按钮添加工具提示,只需添加三行代码:
b1.setToolTipText("Click this button to disable the middle button.");
b2.setToolTipText("This middle button does not react when you click it.");
b3.setToolTipText("Click this button to enable the middle button.");
当程序的用户将光标暂停在程序的任何按钮上时,按钮的工具提示将出现。您可以通过运行ButtonDemo示例来查看这一点,该示例在如何使用按钮、复选框和单选按钮中有解释。这是当光标暂停在ButtonDemo示例中的左按钮上时出现的工具提示的图片。

对于诸如选项卡窗格之类具有多个部分的组件,通常可以根据光标下的组件部分变化工具提示文本以反映该部分。例如,选项卡窗格可以使用此功能来解释当单击光标下的选项卡时会发生什么。当您实现选项卡窗格时,可以在传递给addTab或setToolTipTextAt方法的参数中指定特定于选项卡的工具提示文本。
即使在没有用于设置特定部分工具提示文本的 API 的组件中,您通常也可以自行完成工作。如果组件支持渲染器,则可以在自定义渲染器上设置工具提示文本。表格和树部分提供了由自定义渲染器确定的工具提示文本示例。适用于所有JComponent的另一种方法是创建组件的子类并覆盖其getToolTipText(MouseEvent)方法。
工具提示 API
大多数用于设置工具提示所需的 API 属于JComponent类,并且因此被大多数 Swing 组件继承。更多工具提示 API 可在诸如JTabbedPane之类的单独类中找到。一般来说,这些 API 足以指定和显示工具提示;通常不需要直接处理实现类JToolTip和ToolTipManager。
以下表格列出了JComponent类中的工具提示 API。有关各个组件对工具提示的支持信息,请参阅相关组件的操作指南部分。
JComponent类中的工具提示 API
| 方法 | 目的 |
|---|---|
| setToolTipText(String) | 如果指定的字符串不为 null,则此方法将注册组件具有工具提示,并在显示时为工具提示提供指定的文本。如果参数为 null,则此方法将关闭此组件的工具提示。 |
| String getToolTipText() | 返回先前使用setToolTipText指定的字符串。 |
| String getToolTipText(MouseEvent) | 默认情况下,返回与getToolTipText()返回的相同值。多部分组件如JTabbedPane、JTable和JTree会重写此方法以返回与鼠标事件位置相关联的字符串。例如,选项卡窗格中的每个选项卡可以有不同的工具提示文本。 |
| 获取工具提示位置(MouseEvent) | 返回组件工具提示的左上角出现的位置(在接收组件的坐标系中)。参数是导致工具提示显示的事件。默认返回值为 null,告诉 Swing 系统选择一个位置。 |
使用工具提示的示例
此表列出了一些使用工具提示的示例,并指向这些示例的描述位置。
| 示例 | 描述位置 | 注意事项 |
|---|---|---|
ButtonDemo |
本节和如何使用按钮、复选框和单选按钮 | 使用工具提示为按钮提供说明。 |
IconDemo |
如何使用图标 | 在标签中使用工具提示提供图像的名称和大小信息。 |
TabbedPaneDemo |
如何使用选项卡窗格 | 使用在addTab方法的参数中指定的选项卡特定工具提示文本。 |
TableRenderDemo |
为单元格指定工具提示 | 使用渲染器为表添加工具提示。 |
TableToolTipsDemo |
为单元格指定工具提示, 为列标题指定工具提示 | 使用各种技术为表添加工具提示。 |
TreeIconDemo2 |
自定义树的显示 | 使用自定义渲染器为树添加工具提示。 |
ActionDemo |
如何使用操作 | 为使用Action创建的按钮添加工具提示。 |
如何使用树
原文:
docs.oracle.com/javase/tutorial/uiswing/components/tree.html
使用JTree类,您可以显示分层数据。JTree对象实际上不包含您的数据;它只是提供数据的视图。以下是一棵树的图片:

如前图所示,JTree垂直显示其数据。树显示的每一行都包含一个数据项,称为节点。每棵树都有一个根节点,所有节点都是从根节点派生的。默认情况下,树显示根节点,但您可以另行规定。节点可以有子节点,也可以没有。我们将可以有子节点的节点 无论它们当前是否有子节点 称为分支节点。不能有子节点的节点称为叶节点。
分支节点可以有任意数量的子节点。通常,用户可以通过单击来展开和折叠分支节点 使其子节点可见或不可见。默认情况下,除了根节点外,所有分支节点都是折叠的。程序可以通过监听树展开或树将展开事件来检测分支节点展开状态的变化,如如何编写树展开监听器和如何编写树将展开监听器中所述。
树中的特定节点可以通过 TreePath 标识,TreePath 是一个封装节点及其所有祖先的对象,或者通过其显示行标识,其中显示区域中的每一行显示一个节点。
-
展开的节点是一个非叶节点,当其所有祖先都展开时,将显示其子节点。
-
折叠的节点是隐藏的节点。
-
隐藏的节点是位于折叠祖先下的节点。
本节的其余部分讨论以下主题:
-
创建一棵树
-
响应节点选择
-
自定义树的显示
-
动态更改树
-
创建数据模型
-
树 API
-
使用树的示例
创建一棵树
这是一个应用程序的图片,其顶部显示了一个在滚动窗格中的树。

试一试:
-
点击“启动”按钮以使用 Java™ Web Start 运行树演示(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
展开一个或多个节点。
通过点击项目左侧的圆圈来执行此操作。
-
折叠一个节点。
通过点击已展开节点左侧的圆圈来执行此操作。
下面的代码取自 TreeDemo.java,创建了 JTree 对象并将其放入滚动窗格中:
*//Where instance variables are declared:*
private JTree tree;
...
public TreeDemo() {
...
DefaultMutableTreeNode top =
new DefaultMutableTreeNode("The Java Series");
createNodes(top);
tree = new JTree(top);
...
JScrollPane treeView = new JScrollPane(tree);
...
}
代码创建了一个 DefaultMutableTreeNode 的实例作为树的根节点。然后创建树中的其余节点。之后,创建树,将根节点作为参数传递给 JTree 构造函数。最后,将树放入滚动窗格中,这是一个常见的策略,因为显示完整展开的树会占用太多空间。
这是创建根节点下节点的代码:
private void createNodes(DefaultMutableTreeNode top) {
DefaultMutableTreeNode category = null;
DefaultMutableTreeNode book = null;
category = new DefaultMutableTreeNode("Books for Java Programmers");
top.add(category);
//original Tutorial
book = new DefaultMutableTreeNode(new BookInfo
("The Java Tutorial: A Short Course on the Basics",
"tutorial.html"));
category.add(book);
//Tutorial Continued
book = new DefaultMutableTreeNode(new BookInfo
("The Java Tutorial Continued: The Rest of the JDK",
"tutorialcont.html"));
category.add(book);
//Swing Tutorial
book = new DefaultMutableTreeNode(new BookInfo
("The Swing Tutorial: A Guide to Constructing GUIs",
"swingtutorial.html"));
category.add(book);
*//...add more books for programmers...*
category = new DefaultMutableTreeNode("Books for Java Implementers");
top.add(category);
//VM
book = new DefaultMutableTreeNode(new BookInfo
("The Java Virtual Machine Specification",
"vm.html"));
category.add(book);
//Language Spec
book = new DefaultMutableTreeNode(new BookInfo
("The Java Language Specification",
"jls.html"));
category.add(book);
}
DefaultMutableTreeNode 构造函数的参数是用户对象,它是一个包含或指向与树节点关联数据的对象。用户对象可以是一个字符串,也可以是一个自定义对象。如果你实现了一个自定义对象,你应该实现它的 toString 方法,以便返回要为该节点显示的字符串。JTree 默认使用从 toString 返回的值来渲染每个节点,因此 toString 返回有意义的内容很重要。有时,重写 toString 是不可行的;在这种情况下,你可以重写 JTree 的 convertValueToText 方法,将模型中的对象映射为要显示的字符串。
例如,前面代码片段中使用的 BookInfo 类是一个自定义类,保存了两个数据:一本书的名称和描述该书的 HTML 文件的 URL。toString 方法被实现为返回书名。因此,与 BookInfo 对象关联的每个节点显示一个书名。
注意: 你可以通过在节点的字符串中放置 HTML 标签来指定树节点中的文本格式。有关详细信息,请参阅在 Swing 组件中使用 HTML。
总结一下,您可以通过调用JTree构造函数来创建一棵树,指定实现 TreeNode 的类作为参数。您可能应该将树放在滚动窗格中,以便树不会占用太多空间。您不必做任何事情来使树节点在用户点击时展开和折叠。但是,您必须添加一些代码,以使树在用户选择节点时做出响应 例如,通过点击节点。
响应节点选择
响应树节点选择很简单。您实现一个树选择监听器并在树上注册它。以下代码显示了TreeDemo程序中与选择相关的代码:
*//Where the tree is initialized:*
tree.getSelectionModel().setSelectionMode
(TreeSelectionModel.SINGLE_TREE_SELECTION);
//Listen for when the selection changes.
tree.addTreeSelectionListener(this);
...
public void valueChanged(TreeSelectionEvent e) {
*//Returns the last path element of the selection.*
*//This method is useful only when the selection model allows a single selection.*
DefaultMutableTreeNode node = (DefaultMutableTreeNode)
tree.getLastSelectedPathComponent();
if (node == null)
*//Nothing is selected.*
return;
Object nodeInfo = node.getUserObject();
if (node.isLeaf()) {
BookInfo book = (BookInfo)nodeInfo;
displayURL(book.bookURL);
} else {
displayURL(helpURL);
}
}
前面的代码执行以下任务:
-
获取树的默认
TreeSelectionModel,然后设置它,以便一次最多只能选择一个树节点。 -
在树上注册一个事件处理程序。事件处理程序是一个实现
TreeSelectionListener接口的对象。 -
在事件处理程序中,通过调用树的
getLastSelectedPathComponent方法确定哪个节点被选中。 -
使用
getUserObject方法获取与节点关联的数据。
有关处理树选择事件的更多详细信息,请参见如何编写树选择监听器。
自定义树的显示
这里是一些树节点的图片,由 Java、Windows 和 Mac OS 外观实现绘制。
![]() |
![]() |
![]() |
|---|---|---|
| Java 外观 | Windows 外观 | Mac OS 外观 |
如前面的图所示,树通常为每个节点显示一个图标和一些文本。您可以自定义这些内容,我们将很快展示。
一棵树通常还会执行一些特定外观的绘制,以指示节点之间的关系。您可以以有限的方式自定义这种绘制。首先,您可以使用tree.setRootVisible(true)来显示根节点,或者使用tree.setRootVisible(false)来隐藏它。其次,您可以使用tree.setShowsRootHandles(true)来请求树的顶层节点 根节点(如果可见)或其子节点(如果不可见) 具有可展开或折叠的手柄。
如果你正在使用 Java 外观,你可以自定义是否绘制线条以显示树节点之间的关系。默认情况下,Java 外观在节点之间绘制倾斜线。通过设置树的JTree.lineStyle客户端属性,你可以指定不同的约定。例如,要请求 Java 外观仅使用水平线来分组节点,请使用以下代码:
tree.putClientProperty("JTree.lineStyle", "Horizontal");
要指定 Java 外观不绘制线条,请使用以下代码:
tree.putClientProperty("JTree.lineStyle", "None");
以下快照显示了在使用 Java 外观时设置JTree.lineStyle属性的结果。
![]() |
![]() |
![]() |
|---|---|---|
"倾斜"(默认) |
"水平" |
"无" |
无论外观如何,节点显示的默认图标是由节点是否为叶子以及如果不是叶子,则是否展开决定的。例如,在 Windows 和 Motif 外观实现中,每个叶子节点的默认图标是一个点;在 Java 外观中,默认叶子图标是一个类似纸张的符号。在我们展示的所有外观实现中,分支节点都用类似文件夹的符号标记。一些外观可能对展开的分支和折叠的分支有不同的图标。
你可以轻松更改用于叶子节点、展开的分支节点或折叠的分支节点的默认图标。要做到这一点,首先创建一个DefaultTreeCellRenderer的实例。你总是可以从头开始创建自己的 TreeCellRenderer 实现,重用你喜欢的任何组件。接下来,通过调用渲染器上的以下一个或多个方法来指定要使用的图标:setLeafIcon(用于叶子节点)、setOpenIcon(用于展开的分支节点)、setClosedIcon(用于折叠的分支节点)。如果你希望树不显示某种类型节点的图标,那么为图标指定null。设置好图标后,使用树的setCellRenderer方法指定DefaultTreeCellRenderer绘制其节点。以下是一个示例,取自TreeIconDemo.java:
ImageIcon leafIcon = createImageIcon("images/middle.gif");
if (leafIcon != null) {
DefaultTreeCellRenderer renderer =
new DefaultTreeCellRenderer();
renderer.setLeafIcon(leafIcon);
tree.setCellRenderer(renderer);
}
这是 TreeIconDemo 的屏幕截图:

试一试:
- 点击“启动”按钮以使用Java™ Web Start运行 TreeIconDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
如果要更精细地控制节点图标或提供工具提示,可以通过创建DefaultTreeCellRenderer的子类并重写getTreeCellRendererComponent方法来实现。因为DefaultTreeCellRenderer是JLabel的子类,可以使用任何JLabel方法 例如setIcon 来自定义DefaultTreeCellRenderer。
下面的代码来自TreeIconDemo2.java,创建了一个单元格渲染器,根据节点文本数据中是否包含“教程”一词来变化叶图标。该渲染器也指定了工具提示文本,如粗体行所示。
试试这个:
- 点击“启动”按钮以使用Java™ Web Start运行 TreeIconDemo2(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
*//...where the tree is initialized:*
//Enable tool tips.
ToolTipManager.sharedInstance().registerComponent(tree);
ImageIcon tutorialIcon = createImageIcon("images/middle.gif");
if (tutorialIcon != null) {
tree.setCellRenderer(new MyRenderer(tutorialIcon));
}
...
class MyRenderer extends DefaultTreeCellRenderer {
Icon tutorialIcon;
public MyRenderer(Icon icon) {
tutorialIcon = icon;
}
public Component getTreeCellRendererComponent(
JTree tree,
Object value,
boolean sel,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus) {
super.getTreeCellRendererComponent(
tree, value, sel,
expanded, leaf, row,
hasFocus);
if (leaf && isTutorialBook(value)) {
setIcon(tutorialIcon);
setToolTipText("This book is in the Tutorial series.");
} else {
setToolTipText(null); //no tool tip
}
return this;
}
protected boolean isTutorialBook(Object value) {
DefaultMutableTreeNode node =
(DefaultMutableTreeNode)value;
BookInfo nodeInfo =
(BookInfo)(node.getUserObject());
String title = nodeInfo.bookName;
if (title.indexOf("Tutorial") >= 0) {
return true;
}
return false;
}
}
这是结果:

您可能想知道单元格渲染器是如何工作的。当树绘制每个节点时,JTree及其外观特定实现实际上都不包含绘制节点的代码。相反,树使用单元格渲染器的绘制代码来绘制节点。例如,要绘制一个具有字符串“Java 编程语言”的叶节点,树会要求其单元格渲染器返回一个可以绘制带有该字符串的叶节点的组件。如果单元格渲染器是DefaultTreeCellRenderer,那么它会返回一个标签,该标签绘制默认叶图标,然后是字符串。
单元格渲染器只负责绘制;它无法处理事件。如果要向树添加事件处理程序,需要在树或者仅在选择节点时发生处理时在树的单元格编辑器上注册处理程序。有关单元格编辑器的信息,请参阅概念:编辑器和渲染器。该部分讨论了类似于树单元格编辑器和渲染器的表单元格编辑器和渲染器。
动态更改树
以下图显示了一个名为 DynamicTreeDemo 的应用程序,允许您向可见树添加节点并删除节点。您还可以编辑每个节点中的文本。

该应用程序基于教程读者 Richard Stanford 提供的示例。
试试这个:
- 点击“启动”按钮以使用Java™ Web Start运行 DynamicTreeDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
这是初始化树的代码:
rootNode = new DefaultMutableTreeNode("Root Node");
treeModel = new DefaultTreeModel(rootNode);
treeModel.addTreeModelListener(new MyTreeModelListener());
tree = new JTree(treeModel);
tree.setEditable(true);
tree.getSelectionModel().setSelectionMode
(TreeSelectionModel.SINGLE_TREE_SELECTION);
tree.setShowsRootHandles(true);
通过显式创建树的模型,代码确保树的模型是DefaultTreeModel的一个实例。这样,我们知道树模型支持的所有方法。例如,我们知道可以调用模型的insertNodeInto方法,即使该方法不是TreeModel接口所必需的。
要使树节点中的文本可编辑,我们在树上调用setEditable(true)。当用户完成编辑节点时,模型会生成一个树模型事件,告诉任何监听器,包括JTree,树节点已更改。请注意,虽然DefaultMutableTreeNode有用于更改节点内容的方法,但更改应通过DefaultTreeModel的封装方法进行。否则,将不会生成树模型事件,监听器如树也不会知道更新。
要通知节点更改,我们可以实现一个TreeModelListener。以下是一个检测用户何时为树节点键入新名称的树模型监听器示例:
class MyTreeModelListener implements TreeModelListener {
public void treeNodesChanged(TreeModelEvent e) {
DefaultMutableTreeNode node;
node = (DefaultMutableTreeNode)
(e.getTreePath().getLastPathComponent());
/*
* If the event lists children, then the changed
* node is the child of the node we have already
* gotten. Otherwise, the changed node and the
* specified node are the same.
*/
try {
int index = e.getChildIndices()[0];
node = (DefaultMutableTreeNode)
(node.getChildAt(index));
} catch (NullPointerException exc) {}
System.out.println("The user has finished editing the node.");
System.out.println("New value: " + node.getUserObject());
}
public void treeNodesInserted(TreeModelEvent e) {
}
public void treeNodesRemoved(TreeModelEvent e) {
}
public void treeStructureChanged(TreeModelEvent e) {
}
}
这是Add按钮的事件处理程序使用的代码,用于向树中添加新节点:
treePanel.addObject("New Node " + newNodeSuffix++);
...
public DefaultMutableTreeNode addObject(Object child) {
DefaultMutableTreeNode parentNode = null;
TreePath parentPath = tree.getSelectionPath();
if (parentPath == null) {
//There is no selection. Default to the root node.
parentNode = rootNode;
} else {
parentNode = (DefaultMutableTreeNode)
(parentPath.getLastPathComponent());
}
return addObject(parentNode, child, true);
}
...
public DefaultMutableTreeNode addObject(DefaultMutableTreeNode parent,
Object child,
boolean shouldBeVisible) {
DefaultMutableTreeNode childNode =
new DefaultMutableTreeNode(child);
...
treeModel.insertNodeInto(childNode, parent,
parent.getChildCount());
//Make sure the user can see the lovely new node.
if (shouldBeVisible) {
tree.scrollPathToVisible(new TreePath(childNode.getPath()));
}
return childNode;
}
该代码创建一个节点,将其插入到树模型中,然后(如果适用)请求展开其上方的节点,并滚动树,以便新节点可见。要将节点插入模型中,代码使用DefaultTreeModel类提供的insertNodeInto方法。
创建数据模型
如果DefaultTreeModel不符合您的需求,则需要编写自定义数据模型。您的数据模型必须实现TreeModel接口。TreeModel指定了获取树的特定节点、获取特定节点的子节点数量、确定节点是否为叶子节点、通知模型树变化以及添加和移除树模型侦听器的方法。
有趣的是,TreeModel接口接受任何类型的对象作为树节点。它不要求节点由DefaultMutableTreeNode对象表示,甚至不要求节点实现TreeNode接口。因此,如果TreeNode接口不适合您的树模型,可以自行设计树节点的表示。例如,如果您有一个现有的分层数据结构,您不需要复制它或强制将其转换为TreeNode模式。您只需实现您的树模型,使其使用现有数据结构中的信息即可。
以下图显示了一个名为 GenealogyExample 的应用程序,显示了特定人的后代或祖先。(感谢教程读者 Olivier Berlanger 提供此示例。)
试一试:
- 点击“启动”按钮以使用Java™ Web Start运行 Genealogy 示例(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。

您可以在GenealogyModel.java中找到自定义树模型实现。因为该模型是作为Object子类而不是DefaultTreeModel子类实现的,所以必须直接实现TreeModel接口。这需要实现获取节点信息的方法,例如根节点是哪个以及特定节点的子节点是什么。在GenealogyModel的情况下,每个节点由Person类型的对象表示,这是一个不实现TreeNode的自定义类。
树模型还必须实现用于添加和移除树模型监听器的方法,并且在树的结构或数据发生变化时向这些监听器发出TreeModelEvent。例如,当用户指示GenealogyExample从显示祖先切换到显示后代时,树模型进行更改,然后向其监听器(如树组件)发出事件以通知它们。
如何懒加载子节点
懒加载是应用程序的一个特征,当类的实际加载和实例化延迟到实际使用实例之前的时刻。
通过懒加载它们我们能获得什么?是的,这绝对会增加应用程序的性能。通过懒加载,你可以将内存资源专门用于在实际使用时加载和实例化对象。你还可以加快应用程序的初始加载时间。
你可以利用TreeWillExpandListener接口懒加载树的子节点之一的方法是。例如,你可以在应用程序中声明并加载树的根、祖父和父节点,如下面的代码所示:
让我们将根、祖父和父节点声明如下:
class DemoArea extends JScrollPane
implements TreeWillExpandListener {
.......
.......
private TreeNode createNodes() {
DefaultMutableTreeNode root;
DefaultMutableTreeNode grandparent;
DefaultMutableTreeNode parent;
root = new DefaultMutableTreeNode("San Francisco");
grandparent = new DefaultMutableTreeNode("Potrero Hill");
root.add(grandparent);
parent = new DefaultMutableTreeNode("Restaurants");
grandparent.add(parent);
dummyParent = parent;
return root;
}
你可以像下面的代码所示将上述声明的节点加载到树中:
TreeNode rootNode = createNodes();
tree = new JTree(rootNode);
tree.addTreeExpansionListener(this);
tree.addTreeWillExpandListener(this);
.......
.......
setViewportView(tree);
现在,每当应用程序中可见父节点Restaurants时,你可以懒加载子节点到应用程序中。为此,让我们在一个单独的方法中声明两个子节点,并按照下面的代码调用该方法:
private void LoadLazyChildren(){
DefaultMutableTreeNode child;
child = new DefaultMutableTreeNode("Thai Barbeque");
dummyParent.add(child);
child = new DefaultMutableTreeNode("Goat Hill Pizza");
dummyParent.add(child);
textArea.append(" Thai Barbeque and Goat Hill Pizza are loaded lazily");
}
.......
.......
public void treeWillExpand(TreeExpansionEvent e)
throws ExpandVetoException {
saySomething("You are about to expand node ", e);
int n = JOptionPane.showOptionDialog(
this, willExpandText, willExpandTitle,
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
willExpandOptions,
willExpandOptions[1]);
LoadLazyChildren();
}
请参阅如何编写 Tree-Will-Expand 监听器以获取 Tree-Will-Expand 监听器的描述。
树 API
树 API 非常广泛。以下表格列出了 API 的一部分,重点关注以下类别:
-
与树相关的类和接口
-
创建和设置树
-
实现选择
-
显示和隐藏节点
有关树 API 的更多信息,请参阅JTree的 API 文档,以及tree package中各个类和接口的文档。还请参阅 JComponent 类以获取有关JTree继承自其超类的 API 的信息。
与树相关的类和接口
| 类或接口 | 目的 |
|---|---|
| JTree | 向用户展示树的组件。 |
| TreePath | 表示到节点的路径。 |
DefaultMutableTreeNode | 默认树模型期望其树节点实现的接口,以及默认树模型使用的实现。
| TreeModel DefaultTreeModel | 分别是树模型必须实现的接口和通常使用的实现。 |
|---|---|
| TreeCellRenderer DefaultTreeCellRenderer | 分别是树单元渲染器必须实现的接口和通常使用的实现。 |
| TreeCellEditor DefaultTreeCellEditor | 分别是树单元编辑器必须实现的接口和通常使用的实现。 |
| TreeSelectionModel DefaultTreeSelectionModel | 分别是树选择模型必须实现的接口和通常使用的实现。 |
| TreeSelectionListener TreeSelectionEvent | 用于检测树选择更改的接口和事件类型。有关更多信息,请参见入门指南。 |
| TreeModelListener TreeModelEvent | 用于检测树模型更改的接口和事件类型。有关更多信息,请参见如何编写树模型监听器。 |
| TreeExpansionListener TreeWillExpandListener
TreeExpansionEvent | 用于检测树展开和折叠的接口和事件类型。有关更多信息,请参见如何编写树展开监听器和如何编写树将展开监听器。
| ExpandVetoException | TreeWillExpandListener 可以抛出的异常,指示即将发生的展开/折叠不应发生。有关更多信息,请参阅如何编写 Tree-Will-Expand 监听器。 |
|---|
创建和设置树
| 构造函数或方法 | 目的 |
|---|
| JTree(TreeNode) JTree(TreeNode, boolean)
JTree(Vector) | 创建一棵树。TreeNode 参数指定根节点,由默认树模型管理。TreeModel 参数指定提供数据给表格的模型。此构造函数的无参数版本用于构建器中;它创建包含一些示例数据的树。如果您将Hashtable、对象数组或Vector作为参数指定,则参数将被视为根节点下的节点列表(不显示),并相应地构建模型和树节点。boolean 参数(如果存在)指定树应如何确定节点是否应显示为叶节点。如果参数为 false(默认值),任何没有子节点的节点都会显示为叶节点。如果参数为 true,则只有当其getAllowsChildren方法返回 false 时,节点才是叶节点。 |
| void setCellRenderer(TreeCellRenderer) | 设置绘制每个节点的渲染器。 |
|---|---|
| void setEditable(boolean) void setCellEditor(TreeCellEditor) | 第一个方法设置用户是否可以编辑树节点。默认情况下,树节点不可编辑。第二个方法设置使用哪个自定义编辑器。 |
| void setRootVisible(boolean) | 设置树是否显示根节点。如果树是使用一个接受数据结构的构造函数创建的,则默认值为 false,否则为 true。 |
| void setShowsRootHandles(boolean) | 设置树是否显示其最左侧节点的句柄,让您可以展开和折叠节点。默认值为 false。如果树不显示根节点,则应调用setShowsRootHandles(true)。 |
| void setDragEnabled(boolean) boolean getDragEnabled() | 设置或获取dragEnabled属性,该属性必须为 true 才能在此组件上启用拖放处理。默认值为 false。有关更多详细信息,请参阅拖放和数据传输。 |
实现选择
| 方法 | 目的 |
|---|---|
| void addTreeSelectionListener(TreeSelectionListener) | 注册一个监听器以检测节点何时被选中或取消选中。 |
| void setSelectionModel(TreeSelectionModel) TreeSelectionModel getSelectionModel() | 设置或获取用于控制节点选择的模型。您可以使用setSelectionModel(null)完全关闭节点选择。 |
| void setSelectionMode(int) int getSelectionMode() | 设置或获取当前选定节点的路径。
(在TreeSelectionModel中) | 设置或获取选择模式。该值可以是CONTIGUOUS_TREE_SELECTION、DISCONTIGUOUS_TREE_SELECTION或SINGLE_TREE_SELECTION(均在TreeSelectionModel中定义)。
| 获取最后选定的组件对象 | 获取代表当前选定节点的对象。这相当于在tree.getSelectionPath()返回的值上调用getLastPathComponent。 |
|---|---|
| void setSelectionPath(TreePath) TreePath getSelectionPath() | 设置或获取当前选定节点的路径。 |
| void setSelectionPaths(TreePath[]) TreePath[] getSelectionPaths() | 设置或获取当前选定节点的路径。 |
| void setSelectionPath(TreePath) TreePath getSelectionPath() | 设置或获取当前选定节点的路径。 |
显示和隐藏节点
| 方法 | 目的 |
|---|---|
| void addTreeExpansionListener(TreeExpansionListener) void addTreeWillExpandListener(TreeWillExpandListener) | 注册监听器以检测树节点何时已展开或折叠,或将要展开或折叠。要否决即将发生的展开或折叠,TreeWillExpandListener可以抛出ExpandVetoException。 |
| void expandPath(TreePath) void collapsePath(TreePath) | 展开或折叠指定的树路径。 |
| void scrollPathToVisible(TreePath) | 确保指定路径的节点可见 - 该路径前面的节点已展开,并且节点在滚动窗格的可视区域内。 |
| void makeVisible(TreePath) | 确保指定路径的节点可见 - 该路径前面的节点已展开。该节点可能不会在可视区域内。 |
| void setScrollsOnExpand(boolean) boolean getScrollsOnExpand() | 设置或获取树是否尝试滚动以显示先前隐藏的节点。默认值为true。 |
| void setToggleClickCount(int) int getToggleClickCount() | 设置或获取节点展开或关闭前的鼠标点击次数。默认值为两次。 |
| TreePath getNextMatch(String, int, Position.Bias) | 返回下一个以特定前缀开头的树元素的TreePath。 |
使用树的示例
此表列出了使用JTree的示例以及这些示例的描述位置。
| 示例 | 描述位置 | 备注 |
|---|---|---|
| 树演示 | 创建树, 响应节点选择, 自定义树的显示 | 创建一个响应用户选择的树。还包含用于自定义 Java 外观和感觉线条样式的代码。 |
| 树图标演示 | 自定义树的显示 | 为 TreeDemo 添加自定义叶子图标。 |
| 树图标演示 2 | 自定义树的显示 | 自定义某些叶子节点图标,并为某些树节点提供工具提示。 |
| 动态树演示 | 动态更改树结构 | 演示如何向树中添加和移除节点。还允许编辑节点文本。 |
| 家谱示例 | 创建数据模型 | 实现自定义树模型和自定义节点类型。 |
| 树展开事件演示 | 如何编写树展开监听器 | 展示如何检测节点的展开和折叠。 |
| 树展开事件演示 2 | 如何编写树将展开监听器 | 展示如何否决节点的展开。 |
如果您在 JavaFX 中编程,请参阅树视图。
如何在 Swing 组件中使用 HTML
原文:
docs.oracle.com/javase/tutorial/uiswing/components/html.html
许多 Swing 组件在其 GUI 的一部分中显示文本字符串。默认情况下,组件的文本以单一字体和颜色显示,全部显示在一行上。您可以通过调用组件的setFont和setForeground方法来确定组件文本的字体和颜色。例如,以下代码创建一个标签,然后设置其字体和颜色:
label = new JLabel("A label");
label.setFont(new Font("Serif", Font.PLAIN, 14));
label.setForeground(new Color(0xffffdd));
如果您想在文本中混合字体或颜色,或者想要格式化,如多行,您可以使用 HTML。HTML 格式可以在所有 Swing 按钮、菜单项、标签、工具提示以及使用标签呈现文本的组件(如树和表格)中使用。
要指定组件的文本具有 HTML 格式,只需在文本开头放置<html>标记,然后在其余部分使用任何有效的 HTML。这是在按钮文本中使用 HTML 的示例:
button = new JButton("<html><b><u>T</u>wo</b><br>lines</html>");
这是生成的按钮。 
一个示例:HtmlDemo
一个名为HtmlDemo的应用程序允许您通过在标签上设置文本来玩转 HTML 格式。您可以在HtmlDemo.java中找到此程序的完整代码。这是HtmlDemo示例的图片。

试一试:
-
单击“启动”按钮以使用Java™ Web Start运行 HtmlDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
编辑左侧文本区域中的 HTML 格式,并单击“更改标签”按钮。右侧的标签显示结果。
-
从左侧的文本区域中删除标记。标签的文本将不再被解析为 HTML。
示例 2:ButtonHtmlDemo
让我们看另一个使用 HTML 的示例。ButtonHtmlDemo为三个按钮添加字体、颜色和其他文本格式。您可以在ButtonHtmlDemo.java中找到此程序的完整代码。这是ButtonHtmlDemo示例的图片。

点击“启动”按钮以使用Java™ Web Start运行 ButtonHtmlDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
左右两个按钮具有多行文本和文本样式,并且是使用 HTML 实现的。另一方面,中间的按钮只使用了一行文本、字体和颜色,因此不需要 HTML。以下是指定这三个按钮的文本格式的代码:
b1 = new JButton("<html><center><b><u>D</u>isable</b><br>"
+ "<font color=#ffffdd>middle button</font>",
leftButtonIcon);
Font font = b1.getFont().deriveFont(Font.PLAIN);
b1.setFont(font);
...
b2 = new JButton("middle button", middleButtonIcon);
b2.setFont(font);
b2.setForeground(new Color(0xffffdd));
...
b3 = new JButton("<html><center><b><u>E</u>nable</b><br>"
+ "<font color=#ffffdd>middle button</font>",
rightButtonIcon);
b3.setFont(font);
请注意,我们必须使用<u>标签来使使用 HTML 的按钮中的助记字符“D”和“E”被下划线标记。还要注意,当按钮被禁用时,其 HTML 文本不幸地保持为黑色,而不是变为灰色。(请参考bug #4783068查看是否情况会发生变化。)
本节讨论了如何在普通的非文本组件中使用 HTML。有关主要用于格式化文本的组件的信息,请参阅使用文本组件。
如果你在使用 JavaFX 进行编程,请查看HTML 编辑器。
如何使用模型
原文:
docs.oracle.com/javase/tutorial/uiswing/components/model.html
大多数 Swing 组件都有模型。例如,按钮(JButton)有一个模型(ButtonModel对象),用于存储按钮的状态——其键盘助记符是什么,是否启用,选中或按下等。一些组件有多个模型。例如,列表(JList)使用ListModel来保存列表的内容,并使用ListSelectionModel来跟踪列表的当前选择。
通常你不需要了解组件使用的模型。例如,使用按钮的程序通常直接处理JButton对象,而不处理ButtonModel对象。
那么为什么模型存在呢?最大的原因是它们让你灵活地确定数据的存储和检索方式。例如,如果你正在设计一个在稀疏填充表中显示数据的电子表格应用程序,你可以创建一个针对这种用途进行优化的自定义表模型。
模型还有其他好处。它们意味着数据不会在程序的数据结构和 Swing 组件的数据结构之间复制。此外,模型会自动将更改传播给所有感兴趣的侦听器,使得 GUI 能够轻松与数据保持同步。例如,要向列表添加项目,可以调用列表模型的方法。当模型的数据发生变化时,模型会向JList和任何其他已注册的侦听器发送事件,GUI 相应更新。
虽然 Swing 的模型架构有时被称为模型-视图-控制器(MVC)设计,但实际上并不是这样。 Swing 组件通常是这样实现的,即视图和控制器是不可分割的,由外观提供的单个 UI 对象实现。 Swing 模型架构更准确地描述为可分离的模型架构。如果你对了解更多关于 Swing 模型架构感兴趣,请参阅Swing 架构概述,这是Swing 连接中的一篇文章。
一个示例:Converter
本节介绍了一个名为 Converter 的示例,这是一个不断在公制和美制单位之间转换距离测量的应用程序。你可以运行 Converter(下载 JDK 7 或更高版本)。或者,要自己编译和运行示例,请参考示例索引。
如下图所示,Converter 具有两个滑块,每个滑块都与一个文本字段绑定。这些滑块和文本字段都显示相同的数据——一个距离——但使用两种不同的度量单位。

对于这个程序来说,重要的是确保只有一个模型控制数据的值。有各种方法可以实现这一点;我们通过将其推迟到顶部滑块的模型来实现。底部滑块的模型(一个名为FollowerRangeModel的自定义类的实例)将所有数据查询转发到顶部滑块的模型(一个名为ConverterRangeModel的自定义类的实例)。每个文本字段通过监听值变化的事件处理程序与其滑块保持同步,反之亦然。确保顶部滑块的模型最终决定显示什么距离。
当我们开始实现自定义滑块模型时,我们首先查看了如何使用滑块的 API 部分。它告诉我们所有滑块数据模型必须实现BoundedRangeModel接口。BoundedRangeModel API 文档告诉我们接口有一个名为DefaultBoundedRangeModel的实现类。DefaultBoundedRangeModel的 API 文档显示它是BoundedRangeModel的通用实现。
我们没有直接使用DefaultBoundedRangeModel,因为它将数据存储为整数,而Converter使用浮点数据。因此,我们实现了ConverterRangeModel作为Object的子类。然后,我们实现了FollowerRangeModel作为ConverterRangeModel的子类。
更多信息
要了解各个组件的模型,请参阅各个组件的"How to"页面和 API 文档。以下是一些直接使用模型的示例:
-
除了最简单的表格示例之外,所有示例都实现了自定义表格数据模型。
-
颜色选择器演示在颜色选择器的选择模型上有更改监听器,以便在用户选择新颜色时通知。在 ColorChooserDemo2 中,
CrayonPanel类直接使用颜色选择模型来设置当前颜色。 -
DynamicTreeDemo 示例设置了树模型(为
DefaultTreeModel的一个实例),直接与其交互,并监听其变化。 -
ListDemo 设置列表数据模型(为
DefaultListModel的一个实例)并直接与其交互。 -
SharedModelDemo 定义了一个扩展
DefaultListModel并实现TableModel的SharedDataModel类。一个JList和一个JTable共享SharedDataModel的一个实例,提供模型数据的不同视图。 -
在事件监听器示例中,ListDataEventDemo 直接创建并使用
DefaultListModel。 -
我们的微调器示例创建微调器模型。
-
正如你已经看到的,转换器示例定义了两个自定义滑块模型。
如何使用图标
原文:
docs.oracle.com/javase/tutorial/uiswing/components/icon.html
许多 Swing 组件,如标签、按钮和选项卡窗格,可以用图标进行装饰 一个固定大小的图片。图标是一个遵循Icon接口的对象。Swing 提供了一个特别有用的Icon接口实现:ImageIcon,它可以从 GIF、JPEG 或 PNG 图像绘制图标。
这是一个带有三个标签的应用程序快照,其中两个标签装饰有图标:

该程序使用一个图像图标来包含和绘制黄色斑点。一个语句创建图像图标,另外两个语句将图像图标包含在两个标签中:
ImageIcon icon = createImageIcon("images/middle.gif",
"a pretty but meaningless splat");
label1 = new JLabel("Image and Text", icon, JLabel.CENTER);
...
label3 = new JLabel(icon);
createImageIcon方法(在前面的片段中使用)是我们在许多代码示例中使用的方法。它查找指定的文件并为该文件返回一个ImageIcon,如果找不到该文件则返回null。以下是一个典型的实现:
/** Returns an ImageIcon, or null if the path was invalid. */
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("Couldn't find file: " + path);
return null;
}
}
在前面的片段中,ImageIcon构造函数的第一个参数是相对于当前类位置的,并将解析为绝对 URL。description参数是一个字符串,允许辅助技术帮助视觉障碍用户理解图标传达的信息。
通常,应用程序提供自己的一组用作应用程序一部分的图像,就像我们许多演示中使用的图像一样。您应该使用Class的getResource方法获取图像的路径。这允许应用程序验证图像是否可用,并在图像不可用时提供合理的错误处理。当图像不是应用程序的一部分时,不应使用getResource,而应直接使用ImageIcon构造函数。例如:
ImageIcon icon = new ImageIcon("images/middle.gif",
"a pretty but meaningless splat");
当您向ImageIcon构造函数指定文件名或 URL 时,处理将被阻塞,直到图像数据完全加载或数据位置被证明无效为止。如果数据位置无效(但非空),则仍会成功创建ImageIcon;只是它没有大小,因此不会绘制任何内容。如createImageIcon方法所示,建议在将 URL 传递给ImageIcon构造函数之前首先验证 URL 是否指向现有文件。这样可以在文件不存在时进行优雅的错误处理。如果您希望在图像加载时获得更多信息,可以通过调用其setImageObserver方法在图像图标上注册观察者。
在底层,每个图像图标都使用一个Image对象来保存图像数据。
本节的其余部分涵盖以下主题:
-
更复杂的图像图标示例
-
使用 getResource 加载图像
-
将图像加载到小程序中
-
提高加载图像图标时的感知性能
-
创建自定义图标实现
-
图像图标 API
-
使用图标的示例
更复杂的图像图标示例
这是一个使用六个图像图标的应用程序。其中五个显示缩略图图像,第六个显示全尺寸照片。

试试这个:
-
单击“启动”按钮以使用 Java™ Web Start 运行 IconDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
单击任何缩略图图像以查看全尺寸照片。
-
将鼠标悬停在照片上。将显示一个工具提示,显示照片标题。
IconDemoApp 展示了以下方式中使用的图标:
-
作为附加到按钮的 GUI 元素(按钮上的缩略图图像)。
-
用于显示图像(五张照片)。
照片通过 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("Icon Demo: " + getValue(SHORT_DESCRIPTION).toString());
}
}
使用 getResource 加载图像
大多数情况下,图像图标的数据来自图像文件。您的应用程序的类文件和图像文件可能以多种有效方式配置在文件服务器上。您可能将类文件放在一个 JAR 文件中,或者将图像文件放在一个 JAR 文件中;它们可能在同一个 JAR 文件中,也可能在不同的 JAR 文件中。以下图示说明了这些文件可以配置的几种方式:
![]() |
![]() |
|---|---|
| 类文件与包含 PNG 格式图像文件的图像目录相邻。 | 类文件与 JAR 文件在同一目录中。JAR 文件中包含一个 images 目录,其中包含所有图像。 |
![]() |
![]() |
| 类文件在一个 JAR 文件中,图片在另一个 JAR 文件中。 | 类和图片文件在同一个 JAR 文件中。 |
如果你正在编写真实世界的应用程序,很可能(也建议)将文件放入包中。有关包的更多信息,请参阅创建和使用包中的学习 Java 语言教程。以下是使用名为"omega"的包的一些可能配置:
![]() |
![]() |
|---|---|
类文件在名为omega的目录中。图片在omega/images目录中。 |
类文件在omega目录中。图片在不在omega目录内的 JAR 文件中,但是按照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方法会导致类加载器在程序的类路径中查找目标文件,一旦找到所需文件就会返回一个 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 |
如何使用组合框 | 使用复合边框将线边框与空边框结合在一起。空边框在线和组件内部之间提供空间。 |














浙公网安备 33010602011771号