技术开发 频道

Buoy使得简单Java用户界面的编程变得轻而易举

  正如已经开始看到的,事件处理是 Buoy 与 Swing 最明显的不同之处。事件处理提供了大量灵活性。Buoy 本身的事件集相当丰富,且允许您挑选自己感兴趣的事件,从任何小部件向其他对象发送事件。例如,如果想在 Swing 中捕获鼠标事件,捕获事件的类需要实现 MouseListener 接口。这个接口有 5 个函数需要实现,即使它们就是摆设也必须实现。而且必须使用接口提供的函数名称。更糟的是,函数必须是侦听器接口的公共部分;要么把这作为公共接口的一部分公开,要么创建一个什么都不做、只是包装事件侦听器代码的内部类。

  在 Buoy 中,每个小部件都是 EventSource 。这意味着可以从每个小部件侦听事件。什么类型的事件呢?任何类型都可以。关键的函数是 addEventLink()。这允许您指定类、侦听器以及可选的方法。每当 EventSource 分派这个类或它的子类的事件时,侦听器都会接收到事件,要么是通过一个叫做 processEvent()的方法,要么是通过在开始调用 addEventLink() 时提供的方法名称。提供的函数不能接受参数,也不能接受与指定事件类型兼容的类的对象;父类和接口可以。

  这是一个方便的设置。可以把不同的事件路由到不同的函数或相同的函数。例如,MousePressedEvent 和 MouseReleasedEvent 会被分别处理。在示例程序中,鼠标的按下、释放和拖动分别有不同的线程,如清单 2 所示。注意,这远远超过 Swing 的 MouseListener 所能做的。如果用 Swing 编程的话,就需要实现 MouseListener 和 MouseMotionListener 这两个接口。

  清单2. 只挑感兴趣的事件

1 this.addEventLink(MousePressedEvent.class, this, "mousePressed");
2 this.addEventLink(MouseReleasedEvent.class, this, "mouseReleased");
3 this.addEventLink(MouseDraggedEvent.class, this, "mouseDragged");
4 [...]
5 public void mouseReleased(WidgetMouseEvent ev) {
6     lastCenter = null;
7     dispatchEvent(new FractalChangedEvent(FractalChangedEvent.SLOW));
8     setAntiAliasing(true);
9 }
10

  mouseReleased() 函数只有最少的工作要做。它只是在 mousePressed() 函数之后进行清理,告诉 Fractal 对象到了开始全面重绘的时候了。

  Buoy 的事件处理还有另外一个有趣的特性。如果愿意的话,可以创建新的事件类型。一个事件类型就是一个类。确实如此。它甚至不需要继承任何类或实现什么。它就是一个类。如果这个类的对象被发送到 dispatchEvent(),那么它或它的父类的侦听器就会被调用。在 Swing 中也可以创建新的事件类型,但是完全要自己进行;必须设计 Listener 接口,还要编写自己的代码生成事件并侦听事件。在示例程序中,设计了 Fractal 类,演示了可以相对容易地把事件处理功能加到任何原有的类中。只需要声明一个 FractalViewer 类用来添加侦听器的事件源 EventSource。FractalViewer 类就会把来自事件源(例如 FractalEditor)的事件链接设置到它们的侦听器,如清单 3 所示。

  清单3. 绑定

1 private void tieEvents() {
2     // Set up event handling relations.
3     addEventLink(WindowResizedEvent.class, this, "layoutChildren");
4     addEventLink(WindowResizedEvent.class, panel, "repaint");
5     tieControlEvents();
6     tieFractalEvents();
7     tiePanelEvents();
8 }
9

  定制事件类一般是为了表示用户行为。在 Buoy 中,一般只通过用户行为,而不是系统接口生成事件 —— 除非自己想显式地调用 dispatchEvent() 自行生成事件。当分形对象以某种会造成字段更新的方式变化的时候,所有部件的控制面板都会得到通知。这样,我们发明一个新类 ParameterChangedEvent,用它表示参数已经变化。或者,如果变化的是选中的点的位置或是索引,就发送一个新的 PointChangedEvent。如果行为足够明显的话,那么事件处理器甚至不需要接受参数。作为事件处理的一个示例,请看清单 4,它演示了 FractalEditor 的 parameterChanged() 方法的开始部分。

  清单 4. 参数发生了变化

1 void parameterChanged(ParameterChangedEvent ev) {
2     FractalParameters p = ev.getParams();
3     int v = ev.getValue();
4     switch (ev.getType()) {
5     case ParameterChangedEvent.ALL:
6         maxSlider.setValue(p.getMaxIterations());
7         minSlider.setMaximum(p.getMaxIterations());
8         minSlider.setValue(p.getMinIterations());
9         maxSlider.setMinimum(p.getMinIterations());
10         zoomSlider.setValue(p.getZoom());
11         break;
12     [...]
13

  在这个例子中,用事件处理系统把各种信息前后传递。在以前的版本中,每个类都有对其他每个类的引用,而且乱七八糟的 get 方法是按天排序的。而在目前的版本中,Buoy 的事件处理系统被用来处理各种通知。例如,FractalChangedEvent 类可以用来让代码的其他部分知道对分形的修改,可能是点的数量变化(编辑器用点的数量为点选择器定义正确的 SpinnerNumberModel),或者是需要重绘的通知,如清单 5 所示。

  清单 5. 显然到了重绘的时候

1 public void fractalChanged(FractalChangedEvent e) {
2     switch (e.getType()) {
3     case FractalChangedEvent.REDRAW:
4         repaint();
5         break;
6     }
7 }
8

  学习曲线

  我曾经观察到,学习使用一个 GUI 工具,一下午的时间还不够长。对于 Buoy,我大概需要 6 个小时或者差不多一整个工作日。我确实从更有经验的 Buoy 用户那里得到了很棒的帮助。以前学习 Swing 的经验也是有帮助的,但实际上,我并不认为 Swing 的经验是必需的。Buoy 的文档相当好,而它的简单性确实有帮助。对于基本的 UI 事物,没有太多要学的东西。

  Buoy 的文档并不像 Swing 文档那样完整,但是覆盖了许多细节,而且非常好。另外,源代码也在那儿,所以回答一些关于界面的简单问题非常容易。具有更完整的文档当然是好事。但是,既然这个项目放在 SourceForge 上,所以如果您愿意,您可以编写更多的东西为它做贡献。

  Buoy 的学习曲线比起 Swing 是一个很大的优势。用相当简单的界面就能让大多数界面小部件正确工作。要使用 Buoy 文档中的一个示例:在 Swing 中,JList 要求要么使用静态列表,要么构建一个实现 ListModel 接口的新类。在 Buoy 中,只需向列表中添加项目;在大多数常见情况下,艰巨的工作已经由 Buoy 替您做了。

  Buoy 相当小。完整的发行包中包含源代码、JAR文件和文档,总共不到 1 MB。代码的组织良好,可以容易地找到任何特定的代码段,如果需要调整设计,也不困难。

0
相关文章