Где я должен разместить прослушиватель событий?

Я создал множество объектов, таких как JButton, JLabel, JCheckBox и т. д. И добавил к объекту прослушиватель событий. Так. объект 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);
если слушатель использует другие объекты, эти объекты должны быть созданы до кода слушателя: нет, это неправильно.
JB Nizet 30.05.2019 16:01

но я получил сообщение об ошибке типа «невозможно разрешить», и в сообщении говорилось, что мне нужно создать переменную... что я сделал ошибку?

Y X 30.05.2019 16:04

Где-то в коде, который вы не опубликовали, и который мы, таким образом, не можем прочитать. Отредактируйте свой вопрос. Это должно выглядеть так: Как я могу исправить следующую ошибку? <полная и точная ошибка> в следующем коде <код, который вызывает ошибку, как текст, правильно отформатированный>

JB Nizet 30.05.2019 16:05

Мне очень жаль... Я отредактировал пост..

Y X 30.05.2019 16:15

У вас есть два решения: объявить переменные в классе (т.е. сделать поля темы вместо локальных переменных) или объявить и инициализировать их перед вызовом addActionListener().

JB Nizet 30.05.2019 16:17

Намного лучше сейчас!

GhostCat 30.05.2019 16:18

Кроме того, не устанавливайте границы ваших компонентов. Вместо этого используйте менеджеры компоновки. И изолируйте код обработки базы данных в отдельный класс.

JB Nizet 30.05.2019 16:19

Спасибо за подробный ответ. Я попробую. Спасибо!

Y X 30.05.2019 16:22

Может быть, я немного придирчив, но я не буду использовать флажок для этого. Вы должны использовать переключатель, и кнопки должны быть добавлены в группу переключателей. Это делается для того, чтобы выбор одного параметра автоматически отменял выбор другого (если только вы не хотите запускать несколько запросов одним действием кнопки). Кроме того, это может быть не лучший способ реализовать то, что вам нужно. Ваш обработчик событий WAAAAAY слишком занят.

hfontanez 30.05.2019 17:07

Кроме того, экземпляры объектов TableSummary выглядят очень подозрительно. На мой взгляд, создано слишком много новых объектов. Запустите свое приложение через профилировщик и посмотрите, как работает строка behindList.add(new TableSummary(temp.TABLE_SID, temp.TABLE_NM));. У меня такое чувство, что это может быть горячей точкой.

hfontanez 30.05.2019 18:20
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
10
231
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я не уверен, читали ли вы это, но я бы начал с чтения The Java™ Tutorials на Написание прослушивателей событий. Это руководство очень помогло мне, когда я учился правильно обрабатывать события GUI. Поскольку необходимо обрабатывать так много событий, Java предоставляет основной интерфейс, называемый EventListener, который не имеет методов (это то, что известно как Интерфейс маркера). Каждый JComponent определяет, какое событие он должен обрабатывать. Например, объекты JButton связаны с другим набором событий, чем объекты JFrame.

Лучшее, что вы можете сделать, это понять основную предпосылку того, для чего нужен этот API. Графические интерфейсы должны управляться событиями, потому что не существует эффективного способа зафиксировать взаимодействие пользователя прямо в момент его выполнения. Например, объект JButton должен прослушивать, по крайней мере, события «щелчка». Это должно быть очевидно для вас. Но есть и другие события, о которых вы, вероятно, не подумали. Например, наведите указатель мыши на кнопку, чтобы отобразить подсказку.

На ваш вопрос,

If listener uses other objects, that objects must be created before listener code... What should I do for this?

Есть несколько способов справиться с этим. Я собираюсь показать вам очень простые случаи, которые, надеюсь, вы сможете использовать для расширения. Предположим, у вас есть панель с двумя кнопками 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.");
    }
});

Некоторым людям не очень нравятся анонимные классы, потому что это влияет на читабельность вашего кода. Мне нравится тот факт, что у каждого экземпляра объекта есть свой персонализированный прослушиватель, который выполняет именно ту работу, которую должен выполнять экземпляр объекта, когда происходит действие.

Я думаю, что этот подход должен работать для вашего случая просто отлично. Есть и другие способы, но я думаю, что вы должны начать использовать этот простой подход, пока не получите больше знаний о том, как обрабатывать события. Дайте мне знать, что вы думаете.

Это решение было предложено до того, как ОП отредактировал этот пост. Кода не было, когда он впервые задал свой вопрос. Я думаю, что это оговорка необходима.

hfontanez 30.05.2019 17:09

Спасибо за ваш ответ. Это было полезно!

Y X 30.05.2019 17:22
Ответ принят как подходящий

Вы должны разделить свой 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, должно быть максимально простым, если это лямбда-выражения или анонимные классы: вместо этого создайте закрытый метод и используйте его ссылку:

  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 для передачи параметра, а не вводить его.

Большое спасибо! Этот ответ решает многие проблемы моей программы. Это очень помогло. Огромное спасибо!

Y X 30.05.2019 17:33

@Y X, вы должны использовать шаблоны проектирования для решения подобных проблем. Прослушиватели событий только решают проблему обработки (выполнения) событий. Использование абстракций или других стратегий для разрешения зависимостей во время события — еще одна проблема, которая не должна быть частью слушателей напрямую. Теоретически вы можете создать свой собственный класс источника событий, который мог бы инкапсулировать все эти зависимости при выборе переключателя или флажка.

hfontanez 30.05.2019 18:14

Другие вопросы по теме