我应该在哪里放置事件监听器?

时间:2019-05-30 13:59:25

标签: java swing

我制作了许多对象,例如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);

2 个答案:

答案 0 :(得分:2)

您应该将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);
      });
    });
  }

如您所见,如果您在事件侦听器之前创建了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.");
    }
});

某些人不太喜欢匿名类,因为它会影响代码的可读性。我碰巧喜欢这样一个事实,每个对象的实例都有其自己的个性化侦听器,该侦听器可以精确地执行操作发生时该对象实例需要完成的工作。

我认为这种方法应该适合您的情况。还有其他方法,但是我认为您应该开始使用这种简单的方法,直到您获得有关如何处理事件的更多知识。让我知道你的想法。