我制作了许多对象,例如JButton,JLabel,JCheckBox等。并且我向该对象添加了一个事件侦听器。像这样。 对象obj =新obj(); obj.listneraddActionListener(){}; 但是,如果侦听器使用其他对象,则必须在侦听器代码之前创建该对象。 我在考虑应该先对对象定义进行排序还是将所有侦听器拉到下面。 我该怎么办?
编辑::很抱歉,我没有发布代码,因为代码太大。以下是代码的一部分。我从复选框(chkName,chkAddress,chkType,chkComment)收到错误消息
JButton btnSearch = new JButton("");
btnSearch.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
String str = "";
ResultSet rsSid, rsNM = null;
int count = 0;
if (chkName.isSelected()) {str += "(SELECT " + tableName + "_SID FROM " + tableName + " WHERE " + tableName + "_NM LIKE '%" + txtSearch.getText() + "%') UNION "; ++count;}
if (chkAddress.isSelected()) {str += "(SELECT " + tableName + "_SID FROM " + tableName + " WHERE " + "ADDR LIKE '%" + txtSearch.getText() +"%') UNION "; ++count;}
if (chkType.isSelected()) {str += "(SELECT " + tableName + "_SID FROM " + tableName + " WHERE " + "TYPE LIKE '%" + txtSearch.getText() +"%') UNION "; ++count;}
if (chkComment.isSelected()) {str += "(SELECT " + tableName + "_SID FROM " + tableName + " WHERE " + "COMMENT LIKE '%" + txtSearch.getText() +"%') UNION "; ++count;}
if (count == 0) return;
if (txtSearch.getText() != "")
str = str.substring(0, str.length() - 7) + ';';
else
str = "SELECT * FROM " + tableName;
rsSid = jdbc.executeQuery(conn, str);
try {
behindList.clear();
lstSRmodel.clear();
TableSummary temp = new TableSummary();
while(rsSid.next()) {
for (int i = 1; i <= rsSid.getMetaData().getColumnCount(); ++i) {
temp.TABLE_SID = rsSid.getInt(i);
rsNM = jdbc.executeQuery(conn, "SELECT " + tableName + "_NM FROM " + tableName + " WHERE " + tableName + "_SID = " + temp.TABLE_SID + ";");
if (rsNM.next()) {
temp.TABLE_NM = rsNM.getString(1);
behindList.add(new TableSummary(temp.TABLE_SID, temp.TABLE_NM));
lstSRmodel.addElement(temp.TABLE_NM);
}
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
});
JCheckBox chkAll = new JCheckBox("\uC804\uCCB4");
chkAll.setBounds(14, 75, 131, 27);
contentPane.add(chkAll);
JCheckBox chkName = new JCheckBox("\uC774\uB984");
chkName.setBounds(14, 106, 131, 27);
contentPane.add(chkName);
JCheckBox chkAddress = new JCheckBox("\uC704\uCE58");
chkAddress.setBounds(14, 137, 131, 27);
contentPane.add(chkAddress);
JCheckBox chkType = new JCheckBox("\uD0C0\uC785");
chkType.setBounds(14, 168, 131, 27);
contentPane.add(chkType);
JCheckBox chkComment = new JCheckBox("\uC138\uBD80\uC0AC\uD56D");
chkComment.setBounds(14, 199, 131, 27);
contentPane.add(chkComment);
答案 0 :(得分:2)
您应该将ActionListener
和业务代码分开:您不应在ActionListener
中执行长时间运行的查询,因为它会通过阻塞EDT(事件调度线程)冻结应用程序。
在处理Swing(或其他框架,例如Java FX,SWT等)时,我最好执行以下操作:
那就是(如果愿意,可以将“ 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);
});
});
}
如您所见,如果您在事件侦听器之前创建了Swing组件(chkName
,...),则可能无法在方法中引用它们:您应将其创建为类而不是变量。
否则,您必须在事件侦听器之前创建它们。
此外,尽管未在问题中讨论,但您应该重写查询,因为您正在注入SQL:
"(SELECT " + tableName + "_SID FROM " + tableName + " WHERE " + tableName + "_NM LIKE '%" + txtSearch.getText() + "%') UNION ";
应将'%" + txtSearch.getText() + "%'
替换为?
,并且应该使用PreparedStatement传递参数而不是注入参数。
答案 1 :(得分:1)
我不确定您是否已阅读此书,但我将从阅读Writing Event Listeners上的Java™教程开始。当我学习如何正确处理GUI事件时,本教程对我有很大帮助。由于要处理的事件太多,因此Java提供了名为EventListener
的主接口,该主接口没有方法(这就是所谓的 Marker接口 )。每个JComponent
定义了它必须处理的事件类型。例如,JButton
对象与JFrame
对象所关注的事件集不同。
您能做的最好的事情就是了解为什么需要此API的基本前提。 GUI必须是事件驱动的,因为没有有效的方法可以在操作发生时立即捕获用户交互。例如,JButton
对象必须至少侦听“单击”事件。这对您应该显而易见。但是,您可能还没有想到其他事件。例如,将鼠标悬停在按钮上以显示工具提示。
您的问题
如果侦听器使用其他对象,则必须在侦听器代码之前创建该对象... 我该怎么办?
有几种方法可以解决此问题。我将向您展示一些非常简单的案例,希望您可以继续使用。假设您有一个带有两个按钮的面板,分别为“确定”和“取消”。显然,您知道这两个按钮在单击时将执行不同的功能。对于此示例,他们将要做的工作无关紧要。因此,我将在单击时打印出一些消息。下面的代码段仅显示相关部分。要处理按钮单击,必须将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.");
}
});
某些人不太喜欢匿名类,因为它会影响代码的可读性。我碰巧喜欢这样一个事实,每个对象的实例都有其自己的个性化侦听器,该侦听器可以精确地执行操作发生时该对象实例需要完成的工作。
我认为这种方法应该适合您的情况。还有其他方法,但是我认为您应该开始使用这种简单的方法,直到您获得有关如何处理事件的更多知识。让我知道你的想法。