Swing 的小帮手
创建 Swing 应用程序的辅助库的描述和用法示例
引言
Swing 是主要的 Java GUI 部件工具包。它易于使用,但有些地方可以改进,例如常见默认值或设置的假设,以及在将属性绑定到对象时使用某些类型的集合。
我发现了一些可以改进的领域,并创建了一个名为 SwingHelper 的库。这个库的目标是在使用 Swing 组件时设置通用默认值,以实现代码重用并使方法调用更快、更标准化。
本文旨在学习 Swing 的基础知识以及 SwingHelper 开源库的用法。
背景
正如引言中所述,Swing 是主要的 Java GUI,但它并不是 Java 创建桌面应用程序的唯一选择,我们还可以找到 AWT、SWT、SwingX、JavaFX、Apache Pivot 等等。
AWT 是第一个 Java GUI,Swing 是它的后继者。Swing 的创建是为了提供完全用 Java 编写的丰富组件,这意味着它们不是通过特定于平台的代码实现的,它们也被称为“轻量级”元素,Swing 能够通过使用 Java2D API 进行绘制来渲染自己的组件,而不依赖于原生用户界面工具包。
我选择 Swing 来处理我的项目的原因是它完全用 Java 编写,并且包含在 JVM 中,因此不需要额外的库。对我来说,主要原因是可以使用我想要的“外观和感觉”来创建与操作系统无关的应用程序,这可以是 Java 的默认设置之一,在所有平台上看起来都一样,或者我最喜欢的,使用与当前操作系统相同的设置,所有这些都在一个纯 jar 文件中,可以在任何平台上执行,字面意思是“一次编写,到处运行”。
使用代码
SwingHelper 是什么?
它是一个用 Java 编写的开源库,它是一组通用的组件调用,通过假设默认值来降低复杂性。此外,它还实现了一些 AbstractModel,可以在几秒钟内将数据绑定到组件。
下载库
SwingHelper 是开源的,这意味着您可以免费下载并将其添加到您的项目中。如果您想改进某些内容,可以下载源代码并查看其工作原理。
源代码托管在
https://code.google.com/p/sidereal-libraries/
由于 Google Code 最近更改了
其下载策略,二进制版本可在
https://drive.google.com/folderview?id=0B_QgKAk4BATMaTUxNWNpVlNvcVU&usp=sharing 找到
当前版本为 1.1
SwingHelper 类和方法
基本上,您可以使用这个
类来
- 设置整个应用程序的“外观和感觉”。注意:系统“外观和感觉”被设置为默认值
而不是 Metal。
示例
SwingHelper.setLookAndFeel(SwingHelper.LookAndFeel.NIMBUS);
//or
SwingHelper.setLookAndFeel(SwingHelper.LookAndFeel.MOTIF);
//or
SwingHelper.setLookAndFeel(SwingHelper.LookAndFeel.METAL);
//or
SwingHelper.setLookAndFeel(SwingHelper.LookAndFeel.SYSTEM);
- 更改 Metal“外观和感觉”的主题
示例
SwingHelper.setMetalTheme(SwingHelper.MetalTheme.DEFAULT_METAL);
//or
SwingHelper.setMetalTheme(SwingHelper.MetalTheme.OCEAN);
- 居中一个窗口
示例
SwingHelper.centerWindow(customerJDialog);
- 显示错误消息
示例
SwingHelper.showErrorMessage("Error title", "This is an error message.");
- 显示警告消息
示例
SwingHelper.showWarningMessage("Warning title", "This is a warning message.");
- 显示信息消息
示例
SwingHelper.showInformationMessage("Information title", "This is an information message.");
- 显示一个输入对话框,要求输入一个非空值
示例:
String result =
SwingHelper.askForRequiredTextUsingInputDialog("Text request", "Write your name:");
- 显示一个带有“是/否”、“是/否/取消”或“确定/取消”按钮的确认对话框
示例
String title = "Click on a button to confirm";
String message = "Click on YES button";
String result =
SwingHelper.showConfirmationDialogWithYesNoButtons(title, message);
if(result.equals("yes")){
//write your logic
}
- 显示一个文件选择器,用于选择一个文件夹
示例
String title = “Choose a folder”
String startDirectory = "target/test-classes/com/diegohp/swing/";
File result = SwingHelper.chooseDirectory(title, startDirectory);
//do the logic using 'result' as your folder
- 显示一个文件选择器,用于选择一个文件
示例:
String title = "Choose any file";
File result = SwingHelper.chooseFile(title);
- 显示一个文件选择器,用于选择具有特定扩展名的文件
String title = "Choose a text file";
String fileExtension = ".txt";
File result = SwingHelper.chooseFile(title, fileExtension);
//do your logic with 'result' as your text file
所有这些功能都有多种参数组合(其中一些并未出现在上述示例中),因此您可以为每种情况选择您需要的那个。
最常见的参数是:title(窗口标题)、startDirectory(文件选择器默认打开的文件夹)、fileExtension(要选择的文件扩展名)、startFile(文件选择器默认选择的文件)、lookAndFeel(同名枚举选项之一,包含所有必要信息)、metalTheme(同名枚举选项之一,包含所有必要信息)、message(消息窗口中显示的消息)。
ListComboBoxModel 类
您可以使用此类来
- 创建一个类,它继承自 AbstractListModel 并实现 MutableComboBoxModel。所有方法都已实现,此类对象可直接开箱即用
- AbstractListModel 使该类能够使用 List 作为数据容器(在本例中,是一个带参数类型的 ArrayList)
- MutableComboBoxModel 使该类能够在 UI 组件显示后更改其数据
示例
我们将创建一个窗口,要求用户选择一种语言。
声明一个 JDialog,其中包含一个组合框和一个按钮:
public class LanguageSelectorJDialog extends javax.swing.JDialog{
private javax.swing.JComboBox jComboBoxLanguages;
private javax.swing.JButton jButtonSelect;
}
现在使用 ListComboBoxModel 来创建组合框的模型:
private ListComboBoxModel<String> languagesListComboBoxModel;
注意:在这种情况下,容器的参数化类型设置为 <String>,但它可以是任何您想要的类型,例如 Integer、Long,甚至您的自定义类,例如重写了 toString() 方法的 pojo(组件读取该方法以在 UI 中显示),以返回对用户有用的内容。
接下来,在 JDialog 的构造函数中,我们将填充模型数据:
this.languagesListComboBoxModel.addElement("English");
this.languagesListComboBoxModel.addElement("Español");
现在我们将实现选择按钮的点击逻辑
private void jButtonSelectActionPerformed(java.awt.event.ActionEvent evt) {
//note how easy is to get the selected object of the combo
String selected = (String) this.languagesListComboBoxModel.getSelectedItem();
//then implement your logic
if (selected.equals("English")){
Locale.setDefault(Locale.ENGLISH);
System.out.println("Language set to English");
} else {
Locale.setDefault(new
Locale("es"));
System.out.println("Language set to Spanish");
}
//at the end the JDialog is closed
this.dispose();
}
这是一个 ListComboBoxModel 类的简单用法示例,但请记住参数化类型的好处。在更复杂的示例中,我们不是使用 <String>,而是使用一个名为 Language 的自定义类,它有一个 name(例如 English、Spanish)和一个 language number(例如 345、874),并且语言列表不仅仅局限于 2 种,它们可以更多,基于数据库记录。首先,您需要重写 Language 类的 toString() 方法,使其仅返回名称(因为用户无法理解语言编号),然后只需将语言列表添加到模型中,当用户选择一种语言并单击“选择”按钮时,模型将返回您选择的整个对象。有了这个功能,您就可以避免创建一个辅助列表作为对象索引,从而克服了旧模型的限制,并基于在真实对象列表中选择的值开始搜索,从而节省您的时间、内存、处理器时间和不必要的代码。
您可以看到类似这样的内容
ListTableModel 类
与 ListComboBoxModel 一样,此类共享相同的目标:
- 创建一个继承自 AbstractTableModel 的类,它尽可能多地实现了所有方法,只剩下一个方法需要实现,即 getValueAt(int row, int col),这非常简单,您只需返回相应列的值。在尝试实现 AbstractTableModel 时,您只能完成类的大约 40%,使用 ListTableModel,您可以完成 99% 的实现,并具有灵活可变的逻辑。
- 它有一个 String 列表作为变量来填充列名,因此您可以在组件显示后更改它们
- 实现使用 List 作为数据容器(在本例中,是一个带参数类型的 ArrayList),在表格中显示和编辑信息时非常有用
示例
在这种情况下,我们将创建一个带有客户表格的窗口,当单击一个客户时,将显示一条信息消息,并在表格中增加浏览次数。
首先,让我们创建一个 Customer 的实体类:
public class Customer {
private Long id;
private String name;
private Integer views;
//all the setters an getter
}
现在,让我们创建一个 JDialog,其中包含一个表格、一个模型和一个 Customer 对象来保存选定的客户:
public class CustomerTableModelJDialog extends javax.swing.JDialog {
private javax.swing.JTable jTableCustomers;
private ListTableModel<Customer> customersListTableModel;
private Customer customerSelected;
}
声明模型后,我们需要通过实现剩余方法来创建它的实例。在这种情况下,我们的第一列将显示客户的 Id,第二列显示姓名,第三列显示浏览次数,因此基于此,我们实现返回对象相应值的方法:
this.customersListTableModel = new ListTableModel<Customer>() {
@Override
public Object getValueAt(int row, int col) {
Customer customer = this.objects.get(row);
if (col == 0) {
return customer.getId();
}
if (col == 1) {
return customer.getName();
}
if (col == 2) {
return customer.getViews();
}
return customer.getId() + " -> " + customer.getName();
}
};
现在我们需要声明表格标题的名称,按照我们的顺序将其设置为:
List<String> columnsNames = new ArrayList<String>();
columnsNames.add("Id");
columnsNames.add("Name");
columnsNames.add("Views");
this.customersListTableModel.setColumnNames(columnsNames);
下一步是创建一个监听器来捕获值更改的时刻,并在选择客户时实现一些特定的逻辑:
this.jTableCustomers.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent lse) {
if(!lse.getValueIsAdjusting()){
ListSelectionModel model = jTableCustomers.getSelectionModel();
if(model.getLeadSelectionIndex() >= 0){
customerSelected = ((ListTableModel<Customer>)jTableCustomers.getModel()).getObjectAt(model.getLeadSelectionIndex());
//now we got the customer, let's implement our logic
customerSelected.setViews(customerSelected.getViews() + 1);
//now call fireTableDataChanged so the table updates the data displayed
customersListTableModel.fireTableDataChanged();
//now display some information of the customer
SwingHelper.showInformationMessage("Clicked on a Customer",
"Customer's Id is " + customerSelected.getId() + " and
name is " + customerSelected.getName());
}
}
}
});
模型准备好后,将其设置为表格:
this.jTableCustomers.setModel(this.customersListTableModel);
为了填充表格,我们需要创建一些客户,将它们添加到表格中并更新更改
List<Customer> customers = new
ArrayList<Customer>();
Customer c1 = new Customer();
c1.setId(123456789L);
c1.setName("Harry Mason");
c1.setViews(0);
Customer c2 = new Customer();
c2.setId(987654321L);
c2.setName("James Sunderland");
c2.setViews(0);
customers.add(c1);
customers.add(c2);
this.customersListTableModel.addObjectList(customers);
this.customersListTableModel.fireTableDataChanged();
就是这样。您将看到类似这样的内容:
如果您想查看这些示例的所有代码,请下载源代码并查看测试类,它们包含所有示例。该项目是用 Maven 创建的,因此在构建代码时运行测试应该很容易。
关注点
- 我曾是一名 Java 老师,我用 Swing 教学生如何创建桌面应用程序。Swing 对我的学生来说很容易学习,而且非常有用。从那时起,我一直在为个人和专业项目创建桌面应用程序。在此之前,我曾尝试过其他解决方案,但在尝试了 Swing 之后,我发现它在某些方面非常有趣且更容易,而且能够创建一个在 Windows、Mac 和 Linux 上使用系统界面运行的可执行文件,只需双击一次,这简直是无价的。
-Swing 不需要本地库,也不需要为每个操作系统重新编译,这使得它非常便携、轻便且易于分发。尝试使用 Netbeans 并测试其可视化设计器,我敢打赌您会觉得它很直观。
-SwingHelper 是一个库,其中包含我开始频繁使用的代码,所以我将其放在同一个类中并尝试对其进行优化。我开发这个库的目的是添加更多功能,我将在未来的版本中收集和发布这些功能。
历史
2013 年 10 月 2 日,使用 SwingHelper 1.1 版本创建了本文