Как временно отключить прослушиватели событий в Swing?

У меня есть приложение Swing с моделью и представлением. В представлении (GUI) есть много компонентов, каждый из которых сопоставляет некоторую собственность модельного объекта и отображает его значение.

Теперь есть некоторые компоненты пользовательского интерфейса, которые автоматически запускают обновление некоторых свойств модели, когда их значение изменяется в пользовательском интерфейсе. Это требует от меня перезагрузки полной модели в пользовательском интерфейсе. Таким образом, я вхожу в бесконечный цикл обновления, так как каждая перезагрузка модели в пользовательском интерфейсе запускает перезагрузку другой модели.

У меня есть флаг, указывающий процесс загрузки, который я хотел бы использовать для временного подавления уведомлений слушателя, тогда как поля пользовательского интерфейса устанавливаются из модели. Поэтому мой вопрос:

Есть ли способ глобально временно отключить некоторые компонентные слушатели в Swing без их удаления и повторного подключения?

Ответы

Ответ 1

Вы можете использовать общий базовый класс для своих слушателей и в нем использовать статический метод включения/выключения слушателей:

public abstract class BaseMouseListener implements ActionListener{

    private static boolean active = true;
    public static void setActive(boolean active){
        BaseMouseListener.active = active;
    }

    protected abstract void doPerformAction(ActionEvent e);

    @Override
    public final void actionPerformed(ActionEvent e){
        if(active){
            doPerformAction(e);
        }
    }
}

Ваши слушатели должны были бы реализовать doPerformAction() вместо actionPerformed().

(Это было бы ужасно в корпоративном сценарии, но в модели с одной VM, как в Swing, она должна работать нормально)

Ответ 2

Обычно я использую флаг, указывающий изменения API или пользовательские изменения. Для каждого из слушателей я должен проверить флаг, и если он изменит API, просто верните.

Ответ 3

Во время поиска stackoverflow я нашел этот вопрос. Я думал добавить свое мнение/ответ.

Это действительно плохой идеей, чтобы временно отключить прослушиватели событий в Swing. Если ваш код нарушен (или что-то еще не так), вы не сможете вернуть приложение к жизни - отвечать на пользовательские и другие события.

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

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

В этом вопросе можно найти полную информацию, включая SSCE.

проблема отображения курсора java wait

Ответ 5

Как упоминалось выше, GlassPane полезна в этом отношении. Вот простой пример:

import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.SwingWorker;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;


public class GlassPaneExample extends JFrame implements ActionListener {

private JButton btnDisable;
private JButton btnTestOne;
private JButton btnTestTwo;
private MyGlassPane glass;
private boolean actionAllowed = true;

public GlassPaneExample() {

    // init JFrame graphics
    setBounds(300, 300, 300, 110);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setLayout(new FlowLayout());
    setVisible(true);

    // init buttons
    btnTestOne = new JButton("Button one");
    add(btnTestOne);
    btnTestTwo = new JButton("Button two");
    add(btnTestTwo);
    btnDisable = new JButton("Disable ActionListeners for 2 seconds");
    add(btnDisable);

    // create Glass pane
    glass = new MyGlassPane();
    setGlassPane(glass);

    // add listeners
    btnTestOne.addActionListener(this);
    btnTestTwo.addActionListener(this);
    btnDisable.addActionListener(this);

}

public static void main(String[] args) {
    new GlassPaneExample();
}

@Override
public void actionPerformed(ActionEvent e) {
    JButton src = (JButton)e.getSource();
    if (src.equals(btnDisable)) {

        // setting glasspane visibility to 'true' allows it to receive mouse events
        glass.setVisible(true);
        setCursor(new Cursor(Cursor.WAIT_CURSOR));

        SwingWorker sw = new SwingWorker() {

            @Override
            protected Object doInBackground()
                    throws Exception {
                Thread.sleep(2000);
                return null;
            }

            @Override
            public void done() {
                // set cursor and GlassPane back to default state
                setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                glass.setVisible(false);
                // allow actions to be received again
                actionAllowed = true;
            }
        };
        sw.execute();

    } else if (actionAllowed) {
        if (src.equals(btnTestOne)) {
            JOptionPane.showMessageDialog(this, "BUTTON ONE PRESSED");
        } else if (src.equals(btnTestTwo)) {
            JOptionPane.showMessageDialog(this, "BUTTON TWO PRESSED");
        }
    }
}

class MyGlassPane extends JPanel {

    public MyGlassPane() {

        setOpaque(false);

        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                actionAllowed = false;
            }
        });
    }

    //Draw an cross to indicate glasspane visibility 
    public void paintComponent(Graphics g) {  
      g.setColor(Color.red);  
      g.drawLine(0, 0, getWidth(), getHeight());  
      g.drawLine(getWidth(), 0, 0, getHeight());
   }
}

}

Ответ 6

Этот вопрос похож на аналогичную проблему и не удовлетворительное решение.

Я нашел этот article полезным для критического изучения моих собственных проектов.

Есть ли способ глобально временно отключить некоторые компонентные слушатели в Swing без их удаления и повторного подключения?

Каждый JComponent поддерживает EventListenerList, доступный вашему подклассу. При необходимости вы всегда можете напрямую работать с списком или создавать желаемое поведение в своей пользовательской реализации EventListener