做技術決定
在開發過程中,我不得不去解決一些技術問題並且要做一些技術決定。下面的代碼片段僅僅簡單的進行了解釋,但是他們將在我以後的文章中被詳細描述。在這裡重要的是去理解原型充當的角色。用你的原型去尋找技術問題的解決方案,去測試不常用的APIs,並且保證你的應用程序的性能。
用多層Panels
構建一個例如windows中的畫板的圖形應用程序不是非常復雜的任務。你必須處理鼠標事件、畫線、畫矩形和畫橢圓。還要處理變形功能,比如從一個基礎應用程序到一個專業級的圖形編輯器要具有對圖片的移動,縮放,重新排序、刪除、復制、剪切和粘貼等更多的工作。你也可以想要包含一個可以進行編輯、重新控制大小和文字包裝功能的文字框等等。構建自己的風格文本編輯器是沒有必要的,因為Swing已經提供了一些文本組件。
你怎麼將Swing的文本編輯器和你自己的繪圖組件相集成?我考慮了兩個解決方案。一個是實現一個類似於JTable所用的cell編輯器,但是如果你想改變文本框大小或者移動它就需要一點技巧了。另個一個解決方案是用JDesktopPane,把文本組件放在JInternalFrame之內。
用第二種解決方案的話,Swing已經提供了改變大小和移動功能,但是下面的問題是你怎麼在包含文本注釋的內置frame下繪制圖象?並且你怎麼在JDesktopPane上繪制其他簡單圖形,例如直線、矩形和橢圓?幸運的是,有一個簡單的解決方案,因為JDesktopPane是真正的多層Panel。原型的MainPane類擴展了JDesktopPane,有兩層。它們中的一個包含PaintView自定義組件,允許你繪制簡單圖形。另一層包含文本注釋。當然,如果一個注釋圖片不能被程序獲得,那麼這個解決辦法是沒有意義的。MainPanel的getAnnotatedImage()方法利用下面的代碼做這件事:
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
printAll(g);
g.dispose();
在paint()外部進行繪制
Swing組件的繪制通常都是在paint()內部進行或者在paint()內部調用繪制方法。當用鼠標在屏幕上繪制一個對象,可是,你不想重繪其他組件,因為這將引起應用程序運行效率低下。例如,用戶用鉛筆進行繪制,每個鼠標事件都讓應用繪制一個小線段。在MOUSE_PRESSED和MOUSE_RELEASED之間有上百個MOUSE_DRAGGED事件。
當用戶在屏幕僅僅繪制了一些圖形時,重繪PaintView組件幾百次這樣的事情是不能被接受的。注意一下,PaintView處理大多數的繪制操作並且一個repaint需要所有注釋包括文本注釋進行重繪。正確的解決方案是當每個鼠標事件被處理時在paint()外部利用getGraphics()獲得圖形上下文。
protected void toolAction(MouseEvent e) {
e.consume();
Graphics2D g2 = (Graphics2D) getGraphics();
float zoomFactor = model.getZoomFactor();
g2.scale(zoomFactor, zoomFactor);
float x = e.getX() / zoomFactor;
float y = e.getY() / zoomFactor;
currentTool.action(e.getID(), x, y, g2);
g2.dispose();
}
PaintView組件利用鼠標監聽去處理鼠標事件。上面的方法被每一個事件所調用,委托繪制currentTool對象。當鼠標釋放的時候,repaint()被調用去請求整個組件的刷新。因此,用戶完成圖形對象繪制後paint()僅僅被調用一次。這是注冊鼠標監聽的代碼:
protected void registerListeners() {
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
requestFocus();
currentTool = model.createTool(AbstractTool.DRAW_STYLE);
toolAction(e);
}
}
public void mouseReleased(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
toolAction(e);
model.setLastTool(currentTool);
currentTool = null;
repaint();
}
}
});
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e))
toolAction(e);
}
});
...
}
PaintView類的完整代碼將在以後的文章中講述。上面代碼片段僅僅展示了怎麼利用原型去做技術決定。
總結
原型在應用程序開發過程中有著重要的角色,允許你測試你的想法並且盡早的獲得用戶反饋。我沒有把原型看成是當“真正”開發開始時可以被丟棄的代碼片段。反而,原型應該是你產品或者著應用的基礎。這意味著你應該小心的對它進行編碼,盡管你的類或方法在以後要被重寫。