【问题标题】:WindowListener does not work as expectedWindowListener 无法按预期工作
【发布时间】:2012-04-29 05:56:21
【问题描述】:

我希望我的 GUI 在出现 JOptionPane 时进行一些检查。 因为我找不到任何其他方式,我虽然每次应用程序窗口失去焦点时都可以这样做(它只是检查一个字符串)。出于这个原因,我在我的 JFrame 上添加了以下代码:

appFrame.addWindowListener(new WindowAdapter() {

            @Override
            public void windowLostFocus(WindowEvent e) {
                System.out.println("Focus Lost");

            }
            @Override
            public void windowClosing(WindowEvent e) {
                //some other stuff here that work
            }
});

窗口关闭监听器工作正常。虽然当 JFrame 没有聚焦时,什么也没有发生。每次我从 JFrame 切换到其他窗口时,不应该打印“Focus Lost”吗?另外,显示JOptionPane时会触发这个方法吗?

【问题讨论】:

  • 这似乎是一种倒退的方式。什么触发 optionPane 显示?为什么不在此触发器中调用此代码?
  • 当在应用程序的其他部分发现字符串重复时显示 JOptionPane(与 GUI 没有任何关系)。从该 JOptionPane 中,String 发生了变化,并且该变化需要反映在 JFrame 标题上。由于这些类不进行通信,我认为窗口侦听器将是检查该更改的一种简单方法。
  • 也许说明操作系统在这里会很有用...我不确定 Windows 是否会向窗口发送消息以告知它不再处于活动状态(或者 JVM 是否处理它)
  • 它在 OSX 上无法运行,但我更感兴趣的是它可以在 Windows 上运行。
  • “从那个 JOptionPane 改变了一个字符串” 使目标框架成为 JOptionPane 的所有者/组件,并在字符串消失后立即更改它。顺便说一句-我和其他人可能仍然不清楚您的问题。用更具体的术语来解释这一点可能会有所帮助,比如描述字符串中的内容、它支持的功能......

标签: java swing jframe listener


【解决方案1】:

对我来说,关键是您希望通过更改字符串变量来触发 GUI 更改。我认为解决此问题的最佳方法是使用 PropertyChangeListenerSupport 使 String 变量成为绑定属性。这样,您可以让 GUI 将 PropertyChangeListener 附加到保存 String 变量的类,然后在它发生更改时收到通知,从而允许您适当地更新 GUI。

如果您走这条路,请考虑为观察到的类提供一个 SwingPropertyChangeSupport 字段,以便在 Swing 事件线程上通知侦听器,并希望避免任何 Swing 并发问题。

这是一个简短的例子:

import java.awt.Dimension;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
import javax.swing.event.SwingPropertyChangeSupport;

public class ShowPropertyChangeSupport {
   @SuppressWarnings("serial")
   private static void createAndShowGui() {
      final MainGUI mainGui = new MainGUI("Title");
      final ObservedClass observedClass = new ObservedClass();
      observedClass.addPropertyChangeListener(new PropertyChangeListener() {

         @Override
         public void propertyChange(PropertyChangeEvent pcEvt) {
            if (pcEvt.getPropertyName().equals(ObservedClass.BOUND_PROPERTY)) {
               mainGui.setTitle(pcEvt.getNewValue().toString());
            }
         }
      });

      mainGui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      mainGui.pack();
      mainGui.setLocationRelativeTo(null);
      mainGui.setVisible(true);

      int timerDelay = 6000; // every 6 seconds
      new Timer(timerDelay, new ActionListener() {
         @Override
         public void actionPerformed(ActionEvent arg0) {
            String result = JOptionPane.showInputDialog(mainGui,
                  "Please enter a String", "Set GUI title", JOptionPane.PLAIN_MESSAGE);
            if (result != null) {
               observedClass.setBoundProperty(result);
            }
         }
      }){{setInitialDelay(1000);}}.start();
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

// ** note that I don't like extending JFrame,
// but will do this for sake of example simplicity
class MainGUI extends JFrame {
   public MainGUI(String title) {
      super(title);
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(400, 300);
   }

}

class ObservedClass {
   public static final String BOUND_PROPERTY = "bound property";
   private String boundProperty = "";
   private SwingPropertyChangeSupport spcSupport = new SwingPropertyChangeSupport(
         this);

   public SwingPropertyChangeSupport getSpcSupport() {
      return spcSupport;
   }

   public void setSpcSupport(SwingPropertyChangeSupport spcSupport) {
      this.spcSupport = spcSupport;
   }

   public String getBoundProperty() {
      return boundProperty;
   }

   public void setBoundProperty(String boundProperty) {
      String oldValue = this.boundProperty;
      String newValue = boundProperty;
      this.boundProperty = newValue;
      spcSupport.firePropertyChange(BOUND_PROPERTY, oldValue, newValue);
   }

   public void addPropertyChangeListener(PropertyChangeListener listener) {
      spcSupport.addPropertyChangeListener(listener);
   }

   public void removePropertyChangeListener(PropertyChangeListener listener) {
      spcSupport.removePropertyChangeListener(listener);
   }

}

在我看来,这一切的关键是使用监听器,这样具有绑定属性的类——被监听的字符串——不知道 GUI、监听器和 GUI,同样没有具有绑定属性的类的知识。它们是完全解耦的。

【讨论】:

  • 是的,或者更一般地说,应用程序需要它自己的事件队列——一个注册监听器和调度事件的全局位置。有很多方法可以得到,不知道有没有通用的。
  • 非常感谢这段代码,它真的很有趣。我最终做了类似于观察者模式的事情。我相信我将来会需要这个。
  • @Giannis:这就是所有这些——观察者模式,但有一点额外的装饰,所以你基本上是按照我的建议做的。祝你的项目好运!
  • @HFOE:感谢您提供此代码。我试图通过创建一个“setStringForDisplay(String s)”类型的方法来修改它,该方法在 JPanel 中显示一个字符串。然后我尝试触发它而不是“setTitle”,它没有在 GUI 中更新它。我显然没有正确地遵循某些东西,但会继续修补。
【解决方案2】:

1) JOptionPane / modal JDialog 有模态问题,但如果所有容器都有自己的所有者,模态可能是优势,对于真正的解决方法你需要知道(我将讨论如何测试它)

2) 恕我直言,我不知道你为什么需要那个,为什么我需要知道这个,关于业务规则,你总是需要知道......,如果是美国东部时间完成

【讨论】:

    【解决方案3】:

    我不会详细说明您为什么要做您正在做的事情,但由于以下原因,它并没有像您预期的那样工作:

    WindowAdapter 是一个便利类,因此您可以创建一个侦听器并将其注册用于多种类型的事件。您只为一组事件注册了它,您还需要通过以下方式为焦点事件注册它:Window.addWindowFocusListener()

    WindowAdapter adapter = new WindowAdapter() {
            @Override
            public void windowLostFocus(WindowEvent e) {
                System.out.println("Focus Lost");
            }
            @Override
            public void windowClosing(WindowEvent e) {
                //some other stuff here that work
            }
        };
    appFrame.addWindowListener(adapter);
    appFrame.addWindowFocusListener(adapter);
    

    【讨论】:

    • 最终做了类似于观察者的事情,但这回答了我的问题:)
    猜你喜欢
    • 2013-12-23
    • 2014-12-09
    • 2016-01-13
    • 2020-09-21
    • 2011-08-17
    • 2021-08-12
    • 2019-02-04
    • 2014-12-02
    • 2013-12-02
    相关资源
    最近更新 更多