Как временно отключить прослушиватели событий в Swing? - PullRequest
14 голосов
/ 18 января 2011

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

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

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

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

Ответы [ 6 ]

8 голосов
/ 18 января 2011

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

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().

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

4 голосов
/ 18 мая 2012

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

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

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

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

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

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

4 голосов
/ 18 января 2011

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

3 голосов
/ 03 декабря 2012

Как упомянуто выше, 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());
   }
}

}

3 голосов
/ 18 января 2011

Один из вариантов, который может вам пригодиться, - просто поднять стеклянную панель во время загрузки, чтобы заблокировать события в течение этого времени: http://download.oracle.com/javase/tutorial/uiswing/components/rootpane.html#glasspane

2 голосов
/ 18 января 2011

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

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

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

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

...