有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java如何在Swing中临时禁用事件侦听器?

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

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

我有一个指示加载过程的标志,我想用它暂时抑制侦听器通知,同时从模型中设置UI字段。所以我的问题是:

是否有一种方法可以在Swing中全局临时禁用某些组件的侦听器,而不删除并重新连接它们


共 (6) 个答案

  1. # 1 楼答案

    如上所述,玻璃窗格在这方面很有帮助。 下面是一个简单的例子:

    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. # 2 楼答案

    您可以为侦听器使用公共基类,并在其中使用静态方法打开或关闭侦听器:

    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模型中,它应该可以正常工作)

  3. # 3 楼答案

    在搜索stackoverflow时,我发现了这个问题。我想补充一下我的意见/答案

    在Swing中暂时禁用事件侦听器确实是个坏主意。如果您的代码被破坏(或者出现了其他错误),您可能无法使应用程序恢复生命—响应用户和其他事件

    如果您想放弃(响应但不执行任何操作)用户事件,您可以使用可以忽略事件的玻璃窗格

    如果您的EDT正忙(您必须再次尽可能避免),并且您希望在该时间段内放弃用户操作,您仍然可以使用glasspane并在所有事件响应(glasspane忽略)后使用invokeLater将其删除,以删除窗格

    包括SSCE在内的全部详细信息可在此问题中找到

    java wait cursor display problem

  4. # 5 楼答案

    This question looks like a similar problem and no satisfactory solution to it.

    我发现这有助于批判性地检查我自己的设计

    Is there a way to globally temporarily disable some component's listeners in Swing without removing and reattaching them?

    每个JComponent维护一个^{},您的子类可以访问它。如有必要,您可以始终直接对列表进行操作,或将所需的行为构建到^{}的自定义实现中

  5. # 6 楼答案

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