2 回答
TA贡献1801条经验 获得超8个赞
您应该拆分您ActionListener和您的业务代码:您不应该执行长时间运行的查询,ActionListener因为它会通过阻止 EDT(事件调度线程)来冻结您的应用程序。
在处理 Swing(或其他框架,如 Java FX、SWT 等)时,我最好执行以下操作:
创建 GUI 组件,例如字段,...
配置或“初始化”布局,即将组件添加到它们的父级等等
配置任何事件监听器
那将是(如果您愿意,可以将“init”替换为“configure”):
private JCheckBox checkBox;
MyClass() { // constructor
this.init();
}
void init() {
initComponents();
initEventListeners();
}
void initComponents() {
this.checkBox = new JCheckBox("..."); // or in the constructor if you want final field.
// layout for the parent component
this.add(new JLabel("Label 1"));
this.add(checkBox);
}
void initEventListeners() {
this.checkBox.addActionListener(System.out::println);
}
此外,如果附加到 Swing 组件的事件是 lambda 或匿名类,则应尽可能简单:相反,创建一个私有方法并使用其引用:
btnSearch.addActionListener(this::searchActionListener);
以及我使用另一个线程 ( ForkJoinPool.commonPool()) 在 EDT(事件调度线程)之外执行长时间运行的工作的方法。否则,界面将被冻结。业务方法在外部完成,在另一个对象(business此处称为)中。
应该禁用搜索按钮,否则用户(在那种情况下)可能会向按钮发送垃圾邮件,从而导致数据库出现不必要的问题......
private void searchActionListener(ActionEvent event) {
btnSearch.setEnabled(false); // user should not be able to search while it's running
// extract Swing data before running outside the EDT
String query = txtSearch.getText()
boolean useName = chkName.isSelected();
boolean useAddress = chkAddress.isSelected();
// ... and so on
// do work OUTSIDE the EDT
ForkJoinPool.commonPool().execute(() -> {
Result result = business.find(query, useName, useAddress);
SwingUtilities.invokeLater(() -> {
processResult(result); // do whatever is needed
btnSearch.setEnabled(true);
});
});
}
如您所见,如果您在chkName事件侦听器之前创建了 Swing 组件 ( , ...) ,则您可能不会在方法中引用它们:您应该将它们创建为类的字段而不是变量。
否则,您必须在事件侦听器之前创建它们。
此外,虽然问题中没有讨论,但您应该重写查询,因为您正在注入 SQL:
"(SELECT " + tableName + "_SID FROM " + tableName + " WHERE " + tableName + "_NM LIKE '%" + txtSearch.getText() + "%') UNION ";
'%" + txtSearch.getText() + "%'应该替换为?并且您应该使用 PreparedStatement 来传递参数而不是注入它。
TA贡献1772条经验 获得超6个赞
我不确定您是否读过这篇文章,但我会先阅读 The Java™ Tutorials on Writing Event Listeners。当我学习如何正确处理 GUI 事件时,本教程对我帮助很大。因为要处理的事件太多,所以 Java 提供了EventListener没有方法的调用的主要接口(这就是所谓的标记接口)。每个都JComponent定义了它必须处理的事件类型。例如,JButton对象关注的事件集与JFrame对象不同。
你能做的最好的事情就是理解为什么需要这个 API 的基本前提。GUI 必须是事件驱动的,因为没有有效的方法可以在操作发生的那一刻捕获用户交互。例如,一个JButton对象必须至少监听“点击”事件。这对你来说应该是显而易见的。但还有一些您可能没有想到的其他事件。例如,将鼠标悬停在按钮上以显示工具提示。
对于你的问题,
如果侦听器使用其他对象,则必须在侦听器代码之前创建该对象......我应该为此做什么?
有几种方法可以处理这个问题。我将向您展示非常简单的案例,希望您可以使用这些案例进行扩展。假设您有一个带有两个按钮的面板,OK 和 Cancel。显然,您知道这两个按钮在单击时将执行不同的功能。对于这个例子,他们将要做的工作是无关紧要的。因此,我将在单击时打印出一些消息。下面的代码片段仅显示相关部分。要处理按钮点击,您必须ActionListener向每个按钮添加 。
public class MyActionListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
JButton button = (JButton)e.getSource();
String name = button.getName(); // Assuming you set "OK" and "Cancel" as the names
if (name.equals("OK")
System.out.println("I will comply with your command!");
else
System.out.println("You cancelled your request.");
}
}
然后,您可以像这样向您的按钮添加一个侦听器......
MyActionListener listener = new MyActionListener();
cancelBtn.addActionListener(listener);
okBtn.addActionListener(listener);
该解决方案应该可以正常工作,但扩展性不是很好。如果您要向您的应用程序添加更多按钮,那么只有一个这样的侦听器是不好的,因为您的侦听器会变得过度工作。稍微好一点的解决方案是为每个实例提供自己的侦听器。您可以利用匿名类来做到这一点。这是如何做:
cancelBtn.addActionListener(new MyActionListener(){
public void actionPerformed(ActionEvent e) {
System.out.println("I will comply with your command!");
}
});
okBtn.addActionListener(new MyActionListener(){
public void actionPerformed(ActionEvent e) {
System.out.println("You cancelled your request.");
}
});
有些人不太喜欢匿名类,因为它会影响代码的可读性。我碰巧喜欢这样一个事实,即每个对象的实例都有自己的个性化侦听器,它可以准确地完成对象实例在操作发生时需要完成的工作。
我认为这种方法应该适用于您的情况。还有其他方法,但我认为您应该开始使用这种简单的方法,直到您获得更多关于如何处理事件的知识。让我知道你的想法。
添加回答
举报
