在我们开始绘制一个草图之前需要提出一个概念,这就是在Sktche程序中将增加打算如何管理一张草图的数据,因为这会影响处理事件的位置和方式。我们已经定义了一个应用程序窗口类Sketcher,但是这个类并不是存储草图基本数据的最佳地方。有的时候我们希望把草图保存在一个文件中,串行化是最简单的实现方式。如果我们打算使用串行化存储一张草图,那么在实现SketchFrame类时就不必把所有的东西与建立Sketch的有关数据混在一起。另外,如果我们从GUI定义中分离出定义草图的基本数据,就会使程序更容易实现。这又回到了我们在第11章中提到的MVC技术,它被应用于Swing组件额度定义中。
理论上,我们应该为此目的特别地设计一个类来管理草图数据,这个类就是草图模式。
表示模式类中数据的视图类(viem)将显示草图并处理用户交互,因此,这个类将组合浏览功能和草图的控制器。通常,GUI的建立和操作并不特别指定在SketchFrame类中我们将要处理的视图。这不仅是我们在Sketcher程序中实现操作的方法,而且也是一种很好的途径。
模式对象将包含构成草图文本和图形的混合体。我们将调用模式类SketchModel及描述这个模式的视图类SketchView,但在下一章,我们才把视图加到程序中。下面的示意图将说明在Sketcher中类之间的关系。
应用程序对象将全面负责管理程序中所包含的各种对象之间的连接。只要应用程序类拥有使每个对象都可用的方法,任何可访问应用程序对象的对象都可以与任何其他的对象进行通信,因此应用程序扮演了一个对象之间通信的通道角色。
注意,SketchFrame不是视图类,它只定义应用程序窗口和与之关联的GUI组件。当我们在下一章中创建SketchView对象时,将把SketchView对象插入到SketschFrame对象的内容窗格中,并使用布局管理器来管理内容窗格。由于视图类的定泛独立于应用程序类。所以我们从菜单中分离出了草图视图和用来与应用程序进行交互的其他组件。这样做的好处是显不文档的区域可以独立于应用程序窗口而拥有自己的坐标系统。
为了在Sketcher中实现模式/视图结构,我们需要为模式和视图定义相应的类,至少是一个框架。包含定义草图数据的类可以定义成这种结构形式:
import java.util;
class sketchModel extends observable
{
//Detail of the rest of class to be filled in later...
}
显然在这个类中会有很多操作要做,我们将在以后逐渐将它们添加进来。由于这个类扩展了()Observable类,所以可以利用它把视图类注册为一个观察器,并自动地通告所有的发生变化的视图。在有多个视图时,这个工具将得到视图自己的内容。
我们可以把视图类定义为派生于JComponent的组件。这样将会内置组件操作的所有方法,若需要的话,可以重载其中的任何一个方法。视图类也需要实现fObjectveru接口以便可以用模式对它进行注册。这是一个框架:
import javax.swing.*;
import java.util,*; //For observer
calss sketchview extends JComponent
implements Observer
{
public SketchView(Skctcher theApp)
{
this.theApp=theApp;
}
//Method called by Observable object when it changes
public void update(Observable o,object rectangle)
{
//code to respond to changes in the model...
}
private Sketcher theApp; //The application object
}
为了显示模式的内容,视图肯定需要对它进行访问,因此构造函数要有一个自变量负责把应用程序对象传递给它。由于在视图中存储的是应用程序对象,而不是模式的引用,并且添加到应用程序对象中的一个方法可返回模式的引用,所以我们可以让视图对象独立于模式对象。如果模式表示了一个完全不同的对象,比如加载了一个新文件。我们并不需要改变视图对象,只要新模式把视图注册为观察器,视图就会在发生变化的模式通告时自动地重画草图。
为了把模式与它的视图集成到Sketcher应用程序中,我们需要为Stetcher类增加一些代码:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class Sketcher
{
public static void main(String[] args)
{
theApp = new Sketcher(); // Create the application object
theApp.init(); // ... and initialize it
}
public void init()
{
window = new SketchFrame("Sketcher", this); // Create the app window
Toolkit theKit = window.getToolkit(); // Get the window toolkit
Dimension wndSize = theKit.getScreenSize(); // Get screen size
// Set the position to screen center & size to 2/3 screen size
window.setBounds(wndSize.width/6, wndSize.height/6, // Position
2*wndSize.width/3, 2*wndSize.height/3); // Size
window.addWindowListener(new WindowHandler()); // Add window listener
sketch = new SketchModel(); // Create the model
view = new SketchView(this); // Create the view
sketch.addObserver((Observer)view); // Register the view with the model
window.getContentPane().add(view, BorderLayout.CENTER);
window.setVisible(true);
}
// Return a reference to the application window
public SketchFrame getWindow()
{
return window;
}
// Return a reference to the model
public SketchModel getModel()
{
return sketch;
}
// Return a reference to the view
public SketchView getView()
{
return view;
}
// Handler class for window events
class WindowHandler extends WindowAdapter
{
// Handler for window closing event
public void windowClosing(WindowEvent e)
{
window.dispose(); // Release the window resources
System.exit(0); // End the application
}
}
private SketchModel sketch; // The data model for the sketch
private SketchView view; // The view of the sketch
private static SketchFrame window; // The application window
private static Sketcher theApp; // The application object
}
SketchFrame构造函数需要做如下修改:
public SketchFrame(String title, Sketcher theApp)
{
setTitle(title); // Set the window title
this.theApp = theApp;
// Rest of the constractor as before…
}
需要在SketchFrarme类中增加theApp变量。
private Stretcher theApp.s
在Sketcher类中有些返回应用程序窗口、模式和视图引用的新方法,这些方法的每个都可从能够引用应用程序的地方访问。
建立了模式和视图对象之后,我们把视图注册为模式的观察器这样使模式发生变化时能够通知给视图。然后,我们将View添加到Window对象的内容窗格中,这是主应用程序窗口。由于内容窗格的BorderLayout管理器把它加到了中心,所以view将占据窗格中其余的所有空间。
现在,我们粗略地知道了前进的方向,让我们上路,继续前进。