为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

呼啦一阵风 2022-12-21 16:48:34
我制作了许多对象,例如 JButton、JLabel、JCheckBox 等。我已经为对象添加了一个事件侦听器。像这样。对象 obj = new 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);                                    }        }    });   
查看完整描述

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 来传递参数而不是注入它。


查看完整回答
反对 回复 2022-12-21
?
梦里花落0921

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.");

    }

});

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


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


查看完整回答
反对 回复 2022-12-21
  • 2 回答
  • 0 关注
  • 166 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号