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

发布于 2024-10-12 16:42:10 字数 319 浏览 9 评论 0 原文

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

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

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

有没有办法在 Swing 中全局暂时禁用某些组件的侦听器,而无需删除并重新附加它们?

I've got a Swing application with a model and a view. In the view (GUI) there are a lot of components, each of them mapping to some property of a model object and displaying it's value.

Now there are some UI components that automatically trigger the updating of some model properties when their value changes in the UI. This requires me to reload the complete model in the UI. This way I'm entering an infinite update loop, as every model reload in the UI triggers another model reload.

I have a flag indicating the load process, which I'd like to use to temporarily suppress the listener notifications, while the UI fields are being set from the model. So my question is:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

爱你是孤单的心事 2024-10-19 16:42:10

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

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

You could use a common base class for your listeners and in it, have a static method to turn the listeners on or off:

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);
        }
    }
}

Your listeners would have to implement doPerformAction() instead of actionPerformed().

(This would be awful in an enterprise scenario, but in a single-VM model like in Swing, it should work just fine)

書生途 2024-10-19 16:42:10

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

Normally I use a flag indicating API changes or user changes. For each of the listeners I would check the flag and if it's API changes just return.

秋风の叶未落 2024-10-19 16:42:10

在搜索 stackoverflow 时,我发现了这个问题。我想添加我的意见/答案。

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

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

如果您的 EDT 很忙(您必须尽可能避免这种情况)并且您想放弃该时间段内的用户操作,您仍然可以使用 glasspane 并使用 invokeLater 删除它,以在所有事件响应后删除该窗格(被玻璃板忽略)。

可以在此问题中找到包括 SSCE 在内的完整详细信息。

java等待光标显示问题

While searching stackoverflow, I found this question. I thought to add my opinion/answer.

It is really^inf bad idea to temporarily disable event listeners in Swing. If your code is broken (or something else goes wrong), you may not be able to bring your application back to life - respond to user and other events.

If you want to discard (respond but do nothing) to user events, you may use glass pane which can just ignore the events.

If your EDT is busy (which again you must avoid as much as possible) and you wanted to discard user action for that period, you may still use a glasspane and remove it using invokeLater to remove the pane after all the events have been responded (ignored by the glasspane) to.

Full details including an SSCE can be found in this question.

java wait cursor display problem

ぺ禁宫浮华殁 2024-10-19 16:42:10

可能对您有用的一种选择是在加载时放置一块玻璃板,以阻止在此期间发生事件:
http://download.oracle.com/javase/tutorial/uiswing /components/rootpane.html#glasspane

One option that might work for you is just to put a glass pane up while loading in order to block events during that time:
http://download.oracle.com/javase/tutorial/uiswing/components/rootpane.html#glasspane

司马昭之心 2024-10-19 16:42:10

如上所述,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());
   }
}

}

As mentioned above, the GlassPane is helpful in this regard.
Here is a simple example:

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());
   }
}

}

辞别 2024-10-19 16:42:10

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

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

有没有办法在 Swing 中全局暂时禁用某些组件的侦听器,而无需删除并重新附加它们?

每个 JComponent 都维护一个 EventListenerList,您的子类可以访问它。如有必要,您始终可以直接对列表进行操作,或者将所需的行为构建到 事件监听器

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

I found this article helpful in critically examining my own designs.

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

Every JComponent maintains an EventListenerList, which is accessible to your subclass. If necessary, you can always operate on the list directly or build the desired behavior into your custom implementation of EventListener

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文