【问题标题】:PropertyChangeSupport for SpinnerNumberModelSpinnerNumberModel 的 PropertyChangeSupport
【发布时间】:2011-10-21 15:14:42
【问题描述】:

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

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

sn-p 不起作用:当我单击 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());
    }

}

【问题讨论】:

    标签: java swing propertychanged propertychangesupport propertychangelistener


    【解决方案1】:

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

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

    【讨论】:

    • 1+,因为它可能是比我更好的答案。
    • 谢谢,没问题。有类似 JCheckBox 的编辑器吗? JCheckBox 的属性更改侦听器? @trashgod
    • selected
    • 找到这个:checkBox.getAccessibleContext().addPropertyChangeListener(listener)
    • 这节省了我的时间...为什么我们必须通过编辑器来获取 TextField?
    【解决方案2】:

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

    【讨论】:

    • +1 添加属性更改支持的好例子,它回答了实际问题! :-)
    • @trashgod:是的,但最好是正确解决问题而不是回答问题。您的答案做到了这一点,更重要的是,使用了 很多 更少的代码。 :)
    • @mKorbel a) 使用最简单的组件来显示某些东西在一个例子中 没有错 b) MySpinnerModel 是一个健壮的(或不是)不正确的输入作为它的父
    【解决方案3】:

    星期一早上...不抗拒几个 cmets 的经典时间:-)

    @timaschew

    • 需要 propertyChangeListener,因为它向我展示了该属性的新旧值。” - (挑剔 - 但总是有这种将需求和解决方案分开的强烈冲动 :),我认为它是反过来:在更改通知中,您需要同时访问旧值和新值,propertyChangeEvent/Listener 是支持它的通知类型,可能还有其他的
    • PropertyChangeSupport 不应该用在 观察代码 的一部分上,它应该用在 obervable 的一侧(就像 @Hovercraft 在他的例子):它的唯一责任是管理和通知注册到 observable 的侦听器
    • 偶尔,accessibleContext 为黑客提供了一个钩子 - 然而,它是一个钩子(除非你真的需要支持可访问性,这很可能是这种情况 :-) 与所有黑客一样,这是一个脆弱的解决方案,大多数可能会在未来的某个时候引起疼痛。跟随关于 Action 和 AbstractButton 如何交互的链接更加稳定

    @气垫船

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

    @trashgod

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

    【讨论】:

    • 优秀的分数。我的建议是ad hoc。就像对数据库进行反规范化一样,必须了解并为后果做好准备。
    【解决方案4】:

    http://docs.oracle.com/javase/tutorial/javabeans/writing/properties.html#bound 您必须在设置器中触发ThePropertyChange。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-11
      • 2012-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多