如何暂时禁用Swing中的事件监听器?

14

我有一个包含模型和视图的Swing应用程序。在视图(GUI)中有很多组件,每个组件都映射到模型对象的某个属性并显示它的值。

现在有一些UI组件会在其值在UI中更改时自动触发更新某些模型属性。这要求我重新加载UI中的完整模型。这样做就进入了无限的更新循环,因为UI中的每次模型重新加载都会触发另一个模型重新加载。

我有一个标志来指示加载过程,我想在UI字段从模型中设置时使用该标志来临时抑制监听器通知。那么我的问题是:

有没有一种方法可以全局临时禁用Swing中某些组件的监听器而不必删除和重新附加它们?


2
请参见https://dev59.com/BW445IYBdhLWcg3wwcyg。 - trashgod
谢谢提供链接!看起来是个类似的问题,目前还没有令人满意的解决方案。 - MicSim
6个回答

8
您可以使用一个公共基类来管理您的监听器,并在其中添加一个静态方法来启用或禁用这些监听器:
```html

您可以使用一个公共基类来管理您的监听器,并在其中添加一个静态方法来启用或禁用这些监听器:

```
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这样的单VM模型中,这应该可以正常工作)

4

通常我使用一个标志来指示API变更或用户变更。对于每个监听器,我会检查标志,如果是API变更,则直接返回。


4
在搜索stackoverflow时,我发现了这个问题。我想要添加我的意见/答案。
在Swing中临时禁用事件监听器是一个非常糟糕的主意。如果你的代码出了问题(或者其他什么问题),你可能无法使你的应用程序恢复正常 - 响应用户和其他事件。
如果你想要丢弃(响应但什么也不做)用户事件,你可以使用glass pane来忽略这些事件。
如果你的EDT很忙(你必须尽可能避免),并且你想要在那段时间内丢弃用户操作,你仍然可以使用一个glasspane,并使用invokeLater在所有事件已被响应(由glasspane忽略)后删除该pane。
完整的细节,包括一个SSCE,可以在这个问题中找到。 java wait cursor display problem

3

3
正如上面提到的,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());
   }
}

}


2

这个问题看起来和一个类似的问题,并且没有令人满意的解决方案。

我发现这篇文章对于批判性地检查自己的设计很有帮助。

在Swing中,是否有一种方法可以全局临时禁用某些组件的监听器而不需要删除和重新附加它们?

每个JComponent都维护着一个EventListenerList,它可以被你的子类访问。如果必要,你总是可以直接操作这个列表或者将期望的行为构建到你的EventListener的自定义实现中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接