SpinnerNumberModel 的 PropertyChangeSupport

发布于 2024-11-28 19:17:18 字数 1478 浏览 3 评论 0原文

我想监听 JSpinner 的 SpinnerNumberModel 值的变化。
我创建一个 PropertyChangeSupport 并将模型放入其中。

我需要 propertyChangeListener,因为它向我显示属性的旧值和新值。

该代码片段不起作用:当我单击 JSpinner 时,propertyChange 方法不打印任何内容。
一个简单的 ChangeListener 只给出新值,但我还需要旧值,我怎样才能得到它?

package de.unikassel.jung;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

import javax.swing.JFrame;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;

public class PropertyChangeTest implements PropertyChangeListener {

    public static void main(String[] args) {
        new PropertyChangeTest();
    }

    public PropertyChangeTest() {
        JFrame frame = new JFrame();
        frame.setBounds(100, 100, 450, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        int value = 1;
        int min = 0;
        int max = 10;
        int step = 1;
        SpinnerNumberModel spinnerModel = new SpinnerNumberModel(value, min, max, step);

        PropertyChangeSupport pcs = new PropertyChangeSupport(spinnerModel);
        pcs.addPropertyChangeListener("value", this);

        JSpinner spinner = new JSpinner(spinnerModel);
        frame.getContentPane().add(spinner);
        frame.setVisible(true);
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        System.out.println(evt);
        System.out.println(evt.getSource());
    }

}

I want to listen to the changes of the value of the SpinnerNumberModel for a JSpinner.
I create a PropertyChangeSupport and put the model into it.

I need the propertyChangeListener, because it shows me the old and new value of the property.

The snippet doesn't work: the propertyChange method prints nothing, when I click on the JSpinner.
A simple ChangeListener give only the new value, but I need also the old value, how can I get it?

package de.unikassel.jung;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

import javax.swing.JFrame;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;

public class PropertyChangeTest implements PropertyChangeListener {

    public static void main(String[] args) {
        new PropertyChangeTest();
    }

    public PropertyChangeTest() {
        JFrame frame = new JFrame();
        frame.setBounds(100, 100, 450, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        int value = 1;
        int min = 0;
        int max = 10;
        int step = 1;
        SpinnerNumberModel spinnerModel = new SpinnerNumberModel(value, min, max, step);

        PropertyChangeSupport pcs = new PropertyChangeSupport(spinnerModel);
        pcs.addPropertyChangeListener("value", this);

        JSpinner spinner = new JSpinner(spinnerModel);
        frame.getContentPane().add(spinner);
        frame.setVisible(true);
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        System.out.println(evt);
        System.out.println(evt.getSource());
    }

}

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

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

发布评论

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

评论(4

以酷 2024-12-05 19:17:18

不要听模型,而是听编辑器的 JFormattedTextField,如下所示。

JSpinner spinner = new JSpinner(new SpinnerNumberModel(1, 0, 10, 1));
JSpinner.DefaultEditor editor = (JSpinner.DefaultEditor) spinner.getEditor();
editor.getTextField().addPropertyChangeListener("value", this);

Instead of listening to the model, listen to the editor's JFormattedTextField, as suggested below.

JSpinner spinner = new JSpinner(new SpinnerNumberModel(1, 0, 10, 1));
JSpinner.DefaultEditor editor = (JSpinner.DefaultEditor) spinner.getEditor();
editor.getTextField().addPropertyChangeListener("value", this);
那一片橙海, 2024-12-05 19:17:18

周一早上......这是不抗拒一些评论的经典时间:-)

@timaschew

  • 需要 propertyChangeListener,因为它向我显示属性的旧值和新值。” - (吹毛求疵 - 但是总是有强烈的愿望将需求和解决方案分开:),我认为这是相反的:在更改通知上,您需要访问旧值和新值,propertyChangeEvent/Listener是支持它的通知类型,可能有成为别人
  • PropertyChangeSupport 不应该在观察代码的一部分上使用,它应该在可观察的一侧使用(就像 @Hovercraft 在他的示例中所做的那样) :它的唯一责任是偶尔管理和通知注册到可观察对象的侦听器
  • ,accessibleContext 提供了一个黑客钩子 - 尽管如此,它是一个黑客钩子(除非你真的需要支持可访问性,这很可能是这种情况) :-) 与所有黑客一样,这是一个脆弱的解决方案,很可能会在将来的某个时候造成痛苦。更稳定地关注关于 Action 和 AbstractButton 如何交互的链接

@Hovercraft

  • 通过更丰富的更改通知增强模型是可行的方法(如:我绝对最喜欢的:-)
  • 只是一个小细节:如果你有一个奴隶,让他做所有工作 - PropertyChangeSupport 具有采用旧/新值的方法,无需在可观察对象上创建事件。无论如何,当通知事件中的 newValue 的旧值和新值相等时,它都会被丢弃
  • ,不要使用该参数,而是再次使用 getValue (超级可能拒绝了更改)

@trashgod

哈哈 - 你已经猜到我不这样做类似的解决方案:它打破了封装,因为它依赖于实现细节,所以除非完全控制 JSpinner 创建并且绝对确定其编辑器永远不会更改,否则不要这样做

Monday morning ... classical time for not resisting a couple of comments :-)

@timaschew

  • "need the propertyChangeListener, because it shows me the old and new value of the property." - (nitpicking - but always have this strong urge to separate requirement and solution :), I think it's the other way round: on a change notification you need access to both the old and new value, a propertyChangeEvent/Listener is a notification type which supports it, there might be others
  • PropertyChangeSupport is not supposed to be used on part of the the observing code, it's supposed to be used on the obervable's side (just as @Hovercraft did in his example): it's sole responsibility is manage and notify the listeners registered to the observable
  • occasionally, accessibleContext provides a hook for hacks - nevertheless, it's a hack to hook into it (except you really need to support accessibility, which might well be the case :-) As with all hacks, that's a brittle solution which most probably will cause pain sometime in the future. Much more stable to follow the link about how Action and AbstractButton interact

@Hovercraft

  • enhancing the model with a richer change notification is the way-to-go (as in: my absolute favourite :-)
  • just a small detail: if you have a slave let him do all the work - PropertyChangeSupport has methods which take the old/new value, no need to feed to create an event on the observable. It will be thrown away anyway when old and new are equal
  • for newValue in the notification event, don't use the parameter but instead use getValue again (super might have rejected the change)

@trashgod

haha - you already guessed that I don't like solution: it breaks encapsulation in that it relies on an implementation detail, so don't except when in complete control of the JSpinner creation and are absolutely sure its editor is never changed

醉南桥 2024-12-05 19:17:18

要使 PropertyChangeSupport 工作,您需要调用其 firePropertyChange 方法,但更重要的是,支持对象需要有权访问它正在侦听的属性的 setXXX 方法,并且在该方法中它需要调用 PropertyChangeSupport 的 firePropertyChange 方法。因此,我认为要使您的想法发挥作用,您需要扩展模型的类,为其提供 PropertyChangeSupport 对象,为其提供添加和删除侦听器方法,并确保侦听模型的 setValue 方法中所做的更改,该方法是钥匙。在我的示例中,该方法如下所示:

   @Override
   public void setValue(Object newValue) {
      // store old value and set the new one
      Object oldValue = getValue();
      super.setValue(newValue);

      // construct the event object using these saved values
      PropertyChangeEvent evt = new PropertyChangeEvent(this, VALUE, oldValue,
               newValue);

      // notify all of the listeners
      pcs.firePropertyChange(evt);
   }

这是我使用 PropertyChangeSupport 的示例模型类:

import java.beans.*;
import javax.swing.*;
import javax.swing.event.*;

@SuppressWarnings("serial")
class MySpinnerNumberModel extends SpinnerNumberModel {
   public static final String VALUE = "value";
   private SwingPropertyChangeSupport pcs = new SwingPropertyChangeSupport(this);

   // you will likely need to create multiple constructors to match
   // the ones available to the SpinnerNumberModel class
   public MySpinnerNumberModel(int value, int min, int max, int step) {
      super(value, min, max, step);
   }

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

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

   @Override
   public void setValue(Object newValue) {
      // store old value and set the new one
      Object oldValue = getValue();
      super.setValue(newValue);

      // construct the event object using these saved values
      PropertyChangeEvent evt = new PropertyChangeEvent(this, VALUE, oldValue,
               newValue);

      // notify all of the listeners
      pcs.firePropertyChange(evt);
   }
}

最后是测试类来测试上述类以查看其是否正常工作:

import java.beans.*;
import javax.swing.*;

public class TestSpinnerPropChange {

   private static void createAndShowUI() {
      final MySpinnerNumberModel myModel = new MySpinnerNumberModel(1, 0, 10, 1);
      final JSpinner spinner = new JSpinner(myModel);

      final JTextField oldValueField = new JTextField(10);
      final JTextField newValueField = new JTextField(10);

      JPanel panel = new JPanel();
      panel.add(spinner);
      panel.add(new JLabel("old value:"));
      panel.add(oldValueField);
      panel.add(new JLabel("new value:"));
      panel.add(newValueField);

      myModel.addPropertyChangeListener(new PropertyChangeListener() {
         public void propertyChange(PropertyChangeEvent evt) {
            // checking the property name is overkill here, but is a good habit
            // to get into, especially if listening to more than one property.
            if (evt.getPropertyName().equals(MySpinnerNumberModel.VALUE)) {
               oldValueField.setText(evt.getOldValue().toString());
               newValueField.setText(evt.getNewValue().toString());
            }
         }
      });

      JFrame frame = new JFrame("TestSpinnerPropChange");
      frame.getContentPane().add(panel);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      java.awt.EventQueue.invokeLater(new Runnable() {
         public void run() {
            createAndShowUI();
         }
      });
   }
}

For a PropertyChangeSupport to work you need to call its firePropertyChange method, but more importantly the support object needs to have access to the setXXX method of the property that it is listening to, and in that method it needs to call PropertyChangeSupport's firePropertyChange method. And so I think for your idea to work, you'll need to extend the model's class, give it a PropertyChangeSupport object, give it the add and remove listener methods, and be sure to listen to changes made in the model's setValue method which is key. In my example that method looks like this:

   @Override
   public void setValue(Object newValue) {
      // store old value and set the new one
      Object oldValue = getValue();
      super.setValue(newValue);

      // construct the event object using these saved values
      PropertyChangeEvent evt = new PropertyChangeEvent(this, VALUE, oldValue,
               newValue);

      // notify all of the listeners
      pcs.firePropertyChange(evt);
   }

Here's my sample model class that uses PropertyChangeSupport:

import java.beans.*;
import javax.swing.*;
import javax.swing.event.*;

@SuppressWarnings("serial")
class MySpinnerNumberModel extends SpinnerNumberModel {
   public static final String VALUE = "value";
   private SwingPropertyChangeSupport pcs = new SwingPropertyChangeSupport(this);

   // you will likely need to create multiple constructors to match
   // the ones available to the SpinnerNumberModel class
   public MySpinnerNumberModel(int value, int min, int max, int step) {
      super(value, min, max, step);
   }

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

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

   @Override
   public void setValue(Object newValue) {
      // store old value and set the new one
      Object oldValue = getValue();
      super.setValue(newValue);

      // construct the event object using these saved values
      PropertyChangeEvent evt = new PropertyChangeEvent(this, VALUE, oldValue,
               newValue);

      // notify all of the listeners
      pcs.firePropertyChange(evt);
   }
}

And finally the test class to test out the above class to see if it is working properly:

import java.beans.*;
import javax.swing.*;

public class TestSpinnerPropChange {

   private static void createAndShowUI() {
      final MySpinnerNumberModel myModel = new MySpinnerNumberModel(1, 0, 10, 1);
      final JSpinner spinner = new JSpinner(myModel);

      final JTextField oldValueField = new JTextField(10);
      final JTextField newValueField = new JTextField(10);

      JPanel panel = new JPanel();
      panel.add(spinner);
      panel.add(new JLabel("old value:"));
      panel.add(oldValueField);
      panel.add(new JLabel("new value:"));
      panel.add(newValueField);

      myModel.addPropertyChangeListener(new PropertyChangeListener() {
         public void propertyChange(PropertyChangeEvent evt) {
            // checking the property name is overkill here, but is a good habit
            // to get into, especially if listening to more than one property.
            if (evt.getPropertyName().equals(MySpinnerNumberModel.VALUE)) {
               oldValueField.setText(evt.getOldValue().toString());
               newValueField.setText(evt.getNewValue().toString());
            }
         }
      });

      JFrame frame = new JFrame("TestSpinnerPropChange");
      frame.getContentPane().add(panel);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      java.awt.EventQueue.invokeLater(new Runnable() {
         public void run() {
            createAndShowUI();
         }
      });
   }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文