Swing builder增强
现在让我们来看看对Swing开发者大有裨益的两个转换吧:@Bindable与@Vetoable。在创建Swing UI时通常要检查某些UI元素值的变化,为了做到这一点,常规的手段就是在类的属性值发生变化时通知JavaBean PropertyChangeListener。下面展示了该样板式的Java代码:
2
3 import java.beans.PropertyChangeListener;
4
5 public class MyBean {
6
7 private String prop;
8
9 PropertyChangeSupport pcs = new PropertyChangeSupport(this);
10
11 public void addPropertyChangeListener(PropertyChangeListener l) {
12
13 pcs.add(l);
14
15 }
16
17 public void removePropertyChangeListener(PropertyChangeListener l) {
18
19 pcs.remove(l);
20
21 }
22
23 public String getProp() {
24
25 return prop;
26
27 }
28
29 public void setProp(String prop) {
30
31 pcs.firePropertyChanged("prop", this.prop, this.prop = prop);
32
33 }
34
35 }
还好通过Groovy与@Bindable注解可以极大的简化上面的代码:
class MyBean {
@Bindable String prop
}
再利用上Groovy Swing builder新的bind()方法,定义一个文本输入域并将其绑定到数据模型的属性上:
textField text: bind(source: myBeanInstance, sourceProperty: 'prop')
甚至还可以这样写:
textField text: bind { myBeanInstance.prop }
绑定还可以处理闭包中的简单表达式,如下所示:
bean location: bind { pos.x + ', ' + pos.y }
你或许还有兴趣看看ObservableMap与ObservableList,他们在Map和List上增加了的类似机制。
除了@Bindable以外,还有一个@Vetoable,用于阻止属性的改变。下面来看看Trompetist类,其中的name不允许包含字母‘z’:
2
3 import groovy.beans.Vetoable
4
5 class Trumpetist {
6
7 @Vetoable String name
8
9 }
10
11 def me = new Trumpetist()
12
13 me.vetoableChange = { PropertyChangeEvent pce ->
14
15 if (pce.newValue.contains('z'))
16
17 throw new PropertyVetoException("The letter 'z' is not allowed in a name", pce)
18
19 }
20
21 me.name = "Louis Armstrong"
22
23 try {
24
25 me.name = "Dizzy Gillespie"
26
27 assert false: "You should not be able to set a name with letter 'z' in it."
28
29 } catch (PropertyVetoException pve) {
30
31 assert true
32
33 }
下面来看个完整的使用了绑定的Swing builder示例:
2
3 import groovy.beans.Bindable
4
5 import static javax.swing.JFrame.EXIT_ON_CLOSE
6
7 class TextModel {
8
9 @Bindable String text
10
11 }
12
13 def textModel = new TextModel()
14
15 SwingBuilder.build {
16
17 frame( title: 'Binding Example (Groovy)', size: [240,100], show: true,
18
19 locationRelativeTo: null, defaultCloseOperation: EXIT_ON_CLOSE ) {
20
21 gridLayout cols: 1, rows: 2
22
23 textField id: 'textField'
24
25 bean textModel, text: bind{ textField.text }
26
27 label text: bind{ textModel.text }
28
29 }
30
31 }
下图展示了该脚本运行后的界面,Frame中有个文本框,下面是个label,label上的文本与文本框中的内容绑定在一起了。

在过去几年中,SwingBuilder得到了长足的发展,因此Groovy Swing团队决定基于SwingBuilder和Grails创建一个新项目:Griffon。Griffon意在引入Grails的约定优于配置策略,同时还有其项目结构、插件系统、gant脚本功能等等。
如果你在开发Swing富客户端,请一定要看看Griffon。