源组件(bean)的属性改变支持PropertyChangeSupport和属性改变监听PropertyChangeListener

JavaBean的属性与一般Java程序中所指的属性,或者说与所有面向对象的程序设计语言中对象的属性是一个概念,在程序中的具体体现就是类中的变量。在JavaBean的设计中,按照属性的不同作用又细分为四类:单值属性;索引属性;关联属性;限制属性。
本文主要介绍如何使用PropertyChangeSupport类来支持关联属性事件的触发。
1.关联属性
关联属性,也称之为绑定属性。绑定属性会在属性值发生变化时,通知所有相关的监听器。为了实现一个绑定属性,必须实现两个机制。
1)  无论何时,只要属性的值发生变化,该bean必须发送一个PropertyChange事件给所有已注册的监听器。该变化可能发生在调用set方法时,或者程序的用户做出某种动作时。
2)  为了使感兴趣的监听器能够进行注册,bean必须实现以下两个方法:

void addPropertyChangeListener(PropertyChangeListener listener);
void removePropertyChangeListener(PropertyChangeListener listener);

2.使用PropertyChangeSupport管理监听器
可以通过java.bean包下的PropertyChangeSupport类来管理监听器。要使用这个类,bean必须有一个此类的数据域。

private PropertyChangeSupport changes = new PropertyChangeSupport( this );

 
这样就可以将添加和移除监听器的任务交给这个对象。

public void addPropertyChangeListener(PropertyChangeListener listener) {
    changes .addPropertyChangeListener(listener);
}
 
public void removePropertyChangeListener(PropertyChangeListener listener) {
    changes .removePropertyChangeListener(listener);
}

 
bean的属性发生变化时,使用PropertyChangeSupport对象的firePropertyChange方法,它会将一个事件发送给所有已经注册的监听器。该方法有三个参数:属性的名字、旧的值以及新的值。属性的值必须是对象,如果是简单数据类型,则必须进行包装。

changes .firePropertyChange( "ourString" , oldString, newString);

 
所有注册的监听器实现PropertyChangeListener接口,该接口中只有一个方法。

public void propertyChange(PropertyChangeEvent e);

bean的属性值发生变化时,该方法中的代码就会被触发。可以通过

e.getOldValue();
e.getNewValue();

来得到changes.firePropertyChange("ourString", oldString, newString);中的oldStringnewString
3.为什么要使用PropertyChangeSupport
使用这个类管理监听器的好处是,它是线程安全的。如果使用一个循环体来set Bean的属性,则这个类可以保证所有监听器执行触发事件的有序。
还有一个好处是,这个类支持fire带索引的属性改变事件(详见java.bean.IndexedPropertyChangeEvent)。此时向注册的监听器发送一个PropertyChangeEvent的方法为:

void fireIndexedPropertyChange(String PropertyName, int index,Object oldValue,Object newValue);

4.示例
MyBoundBean(具体代码见附件)是一个JavaBean,我们关注它的唯一一个属性ourString的变化情况,它的初始值是Hello。并通过PropertyChange类来管理监听器。注意在set方法中会调用firePropertyChange方法。
MyBoundBean.java

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
 
public class MyBoundBean {
    String ourString = "Hello" ;
 
    private PropertyChangeSupport changes = new PropertyChangeSupport( this );
 
    public void setString(String newString) {
       String oldString = ourString ;
       ourString = newString;
       changes .firePropertyChange( "ourString" , oldString, newString);
    }
 
    public String getString() {
       return ourString ;
    }
 
    public void addPropertyChangeListener(PropertyChangeListener listener) {
       changes .addPropertyChangeListener(listener);
    }
 
    public void removePropertyChangeListener(PropertyChangeListener listener) {
       changes .removePropertyChangeListener(listener);
    }
}

 
MyCallBound1类(具体代码见附件)是它的一个监听器。整个动作是这样的,点击jButton1jButton2会触发Buttonaction事件,将jButton1test设置为文本框中的内容,同时,目标beanourString属性的内容也会设置为文本框中的内容。

MyBoundBean b = new MyBoundBean();
public void actionPerformed(ActionEvent e) {
    jButton1 .setText( textBox .getText());
    b .setString( textBox .getText());
}

 
目标bean的属性一改变(注意,初始值是"Hello"),将会触发propertyChange方法的执行。将文本框的内容设置为目标beanourString属性的旧值,同时,将jButton2test设置成目标beanourString属性的新值。

public void propertyChange(PropertyChangeEvent e) {
    if (e.getSource() == b ) {
       textBox .setText(e.getOldValue().toString());
       jButton2 .setText(e.getNewValue().toString());
    }
}

 
 
如果不实现PropertyChangeListener接口的话,可以使用匿名内部类来达到同样的效果。(具体代码见附件MyCallBound2.java

MyBoundBean b = new MyBoundBean();
b .addPropertyChangeListener( new PropertyChangeListener() {
    public void propertyChange(PropertyChangeEvent e) {
       // 这样一来,我们就可以用自己定义的名字实现事件
       ourString_propertyChange(e);
    }
});

 
这样一来,我们就可以用自己定义的名字实现事件。

void ourString_propertyChange(PropertyChangeEvent e) {
    if (e.getSource() == b ) {
       textBox .setText(e.getOldValue().toString());
       jButton2 .setText(e.getNewValue().toString());
    }
}

5.参考资料
[1]Core Java2,Volume-Advanced Features
[2]R.J. Lorimer, Beans: Use PropertyChangeSupport When Supporting PropertyChangeListeners,
[url]http://www.javalobby.org/java/forums/t19476.html[/url]

本文出自 “子 孑” 博客,请务必保留此出处http://zhangjunhd.blog.51cto.com/113473/36838

 

源代码如下:

package com.hexun.sample;

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

public class MyBoundBean {
	String ourString = "Hello";

	private PropertyChangeSupport changes = new PropertyChangeSupport(this);

	public void setString(String newString) {
		String oldString = ourString;
		ourString = newString;
		changes.firePropertyChange("ourString", oldString, newString);
	}

	public String getString() {
		return ourString;
	}

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

	public void removePropertyChangeListener(PropertyChangeListener listener) {
		changes.removePropertyChangeListener(listener);
	}
}
package com.hexun.sample;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;

@SuppressWarnings("serial")
public class MyCallBound1 extends JFrame implements ActionListener,
		PropertyChangeListener {
	JPanel contentPane;

	JButton jButton1 = new JButton();

	JButton jButton2 = new JButton();

	JTextField textBox = new JTextField();

	MyBoundBean b = new MyBoundBean();

	public static void main(String[] args) {
		MyCallBound1 frame = new MyCallBound1();// 创建窗口类
		frame.setVisible(true); // 显示窗口类
	}

	// 窗口类构造器
	public MyCallBound1() {
		jbInit(); // 初始化
	}

	// 控件初始化
	private void jbInit() {
		contentPane = (JPanel) this.getContentPane();
		// 因为在声明该类时使用了extends命令,表示该类是JFrame的继承类,
		// 所以可以使用this关键字
		contentPane.setLayout(null);
		this.setSize(new Dimension(400, 200));
		this.setTitle("关联属性");
		textBox.setBounds(10, 20, 150, 25);
		jButton1.setText("jButton1");
		jButton1.setBounds(170, 20, 120, 25);
		jButton2.setText("jButton2");
		jButton2.setBounds(170, 80, 120, 25);
		jButton1.addActionListener(this);// 加入Action事件
		jButton2.addActionListener(this);// 加入Action事件
		contentPane.add(textBox); // 加入按钮
		contentPane.add(jButton1); // 加入按钮
		contentPane.add(jButton2);// 加入按钮
		b.addPropertyChangeListener(this);	// 加入属性变化侦听器
	}

	// 关联属性事件的实现代码
	public void propertyChange(PropertyChangeEvent e) {
		if (e.getSource() == b) {
			textBox.setText(e.getOldValue().toString());
			jButton2.setText(e.getNewValue().toString());
		}
	}

	protected void processWindowEvent(WindowEvent e) {
		if (e.getID() == WindowEvent.WINDOW_CLOSING) {
			System.exit(0);
		}
	}

	// 这是动作侦听器实现代码
	// 如果使用了implements ActionListener
	// 这个代码就必须要实现
	public void actionPerformed(ActionEvent e) {
		jButton1.setText(textBox.getText());
		b.setString(textBox.getText());
	}
}
package com.hexun.sample;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;

//这是不使用直接实现PropertyChangeListener接口
//而是使用myBoundBean内重载的addPropertyChangeListener
//方法实现关联侦听器的方式
@SuppressWarnings("serial")
public class MyCallBound2 extends JFrame implements ActionListener {
	JPanel contentPane;

	JButton jButton1 = new JButton();

	JButton jButton2 = new JButton();

	JTextField textBox = new JTextField();

	MyBoundBean b = new MyBoundBean();

	public static void main(String[] args) {
		MyCallBound2 frame = new MyCallBound2();// 创建窗口类
		frame.setVisible(true); // 显示窗口类
	}

	// 窗口类构造器
	public MyCallBound2() {
		jbInit(); // 初始化
	}

	// 控件初始化
	private void jbInit() {
		contentPane = (JPanel) this.getContentPane();
		// 因为在声明该类时使用了extends命令,表示该类是JFrame的继承类,
		// 所以可以使用this关键字
		contentPane.setLayout(null);
		this.setSize(new Dimension(400, 200));
		this.setTitle("关联属性");
		textBox.setBounds(10, 20, 150, 25);
		jButton1.setText("jButton1");
		jButton1.setBounds(170, 20, 120, 25);
		jButton2.setText("jButton2");
		jButton2.setBounds(170, 80, 120, 25);
		jButton1.addActionListener(this);// 加入Action事件
		jButton2.addActionListener(this);// 加入Action事件
		contentPane.add(textBox); // 加入按钮
		contentPane.add(jButton1); // 加入按钮
		contentPane.add(jButton2);// 加入按钮

		// 加入属性变化侦听器
		b.addPropertyChangeListener(new PropertyChangeListener() {
			public void propertyChange(PropertyChangeEvent e) {
				// 这样一来,我们就可以用自己定义的名字实现事件
				ourString_propertyChange(e);
			}
		});
	}

	// 关联属性的实现代码
	void ourString_propertyChange(PropertyChangeEvent e) {
		if (e.getSource() == b) {
			textBox.setText(e.getOldValue().toString());
			jButton2.setText(e.getNewValue().toString());
		}
	}

	protected void processWindowEvent(WindowEvent e) {
		if (e.getID() == WindowEvent.WINDOW_CLOSING) {
			System.exit(0);
		}
	}

	// 这是动作侦听器实现代码
	// 如果使用了implements ActionListener
	// 这个代码就必须要实现
	public void actionPerformed(ActionEvent e) {
		jButton1.setText(textBox.getText());
		b.setString(textBox.getText());
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值