Java基础之扩展GUI——高亮元素、上下文菜单、移动旋转元素、自定义颜色(Sketcher 10)

窗口应用程序。

本例在上一版的基础上实现了高亮元素、移动元素、上下文菜单、旋转元素、设置自定义颜色。

1、自定义常量包:

 // Defines application wide constants
package Constants;
import java.awt.*;
import javax.swing.*;
import java.awt.image.BufferedImage; public class SketcherConstants {
// Path for images
public final static String imagePath = "E:/JavaProject/BeginningJava/Images/"; // Toolbar icons
public final static Icon NEW24 = new ImageIcon(imagePath + "New24.gif");
public final static Icon OPEN24 = new ImageIcon(imagePath + "Open24.gif");
public final static Icon SAVE24 = new ImageIcon(imagePath + "Save24.gif");
public final static Icon SAVEAS24 = new ImageIcon(imagePath + "SaveAs24.gif");
public final static Icon PRINT24 = new ImageIcon(imagePath + "Print24.gif"); public final static Icon LINE24 = new ImageIcon(imagePath + "Line24.gif");
public final static Icon RECTANGLE24 = new ImageIcon(imagePath + "Rectangle24.gif");
public final static Icon CIRCLE24 = new ImageIcon(imagePath + "Circle24.gif");
public final static Icon CURVE24 = new ImageIcon(imagePath + "Curve24.gif");
public final static Icon TEXT24 = new ImageIcon(imagePath + "Text24.gif"); public final static Icon RED24 = new ImageIcon(imagePath + "Red24.gif");
public final static Icon GREEN24 = new ImageIcon(imagePath + "Green24.gif");
public final static Icon BLUE24 = new ImageIcon(imagePath + "Blue24.gif");
public final static Icon YELLOW24 = new ImageIcon(imagePath + "Yellow24.gif");
public final static ImageIcon CUSTOM24 = new ImageIcon(new BufferedImage(24, 24, BufferedImage.TYPE_INT_ARGB)); // Menu item icons
public final static Icon NEW16 = new ImageIcon(imagePath + "new16.gif");
public final static Icon OPEN16 = new ImageIcon(imagePath + "Open16.gif");
public final static Icon SAVE16 = new ImageIcon(imagePath + "Save16.gif");
public final static Icon SAVEAS16 = new ImageIcon(imagePath + "SaveAs16.gif");
public final static Icon PRINT16 = new ImageIcon(imagePath + "print16.gif"); public final static Icon LINE16 = new ImageIcon(imagePath + "Line16.gif");
public final static Icon RECTANGLE16 = new ImageIcon(imagePath + "Rectangle16.gif");
public final static Icon CIRCLE16 = new ImageIcon(imagePath + "Circle16.gif");
public final static Icon CURVE16 = new ImageIcon(imagePath + "Curve16.gif");
public final static Icon TEXT16 = new ImageIcon(imagePath + "Text16.gif"); public final static Icon RED16 = new ImageIcon(imagePath + "Red16.gif");
public final static Icon GREEN16 = new ImageIcon(imagePath + "Green16.gif");
public final static Icon BLUE16 = new ImageIcon(imagePath + "Blue16.gif");
public final static Icon YELLOW16 = new ImageIcon(imagePath + "Yellow16.gif");
public final static ImageIcon CUSTOM16 = new ImageIcon(new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB)); // Element type definitions
public final static int LINE = 101;
public final static int RECTANGLE = 102;
public final static int CIRCLE = 103;
public final static int CURVE = 104;
public final static int TEXT = 105; // Initial conditions
public final static int DEFAULT_ELEMENT_TYPE = LINE;
public final static Color DEFAULT_ELEMENT_COLOR = Color.BLUE;
public final static Color HIGHLIGHT_COLOR = Color.MAGENTA;
public final static Font DEFAULT_FONT = new Font("Serif", Font.BOLD, 12); // Font point size range specification
public final static int POINT_SIZE_MIN = 8; // Minimum font point size
public final static int POINT_SIZE_MAX = 24; // Maximum font point size
public final static int POINT_SIZE_STEP = 2; // Point size step // Operating modes
public final static String NORMAL = "Normal";
public final static String MOVE = "Move";
public final static String ROTATE = "Rotate";
}

2、窗口框架:

 // Frame for the Sketcher application
import javax.swing.*;
import javax.swing.border.*;
import java.awt.event.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.font.TextLayout;
import java.awt.geom.Rectangle2D; import static java.awt.event.InputEvent.*;
import static java.awt.Color.*;
import static Constants.SketcherConstants.*;
import static javax.swing.Action.*; @SuppressWarnings("serial")
public class SketcherFrame extends JFrame implements ActionListener {
// Constructor
public SketcherFrame(String title, Sketcher theApp) {
setTitle(title); // Set the window title
this.theApp = theApp; // Save app. object reference
setJMenuBar(menuBar); // Add the menu bar to the window
setDefaultCloseOperation(EXIT_ON_CLOSE); // Default is exit the application setCustomIconColor(CUSTOM16,customColor); // Setup small custom color icon
setCustomIconColor(CUSTOM24,customColor); // Setup large custom color icon
createFileMenu(); // Create the File menu
createElementMenu(); // Create the element menu
createColorMenu(); // Create the element menu
optionsMenu = new JMenu("Options"); // Create options menu
optionsMenu.setMnemonic('O'); // Create shortcut
menuBar.add(optionsMenu); // Add options to menu bar createPopupMenu(); // Create popup // Add the font choice item to the options menu
fontItem = new JMenuItem("Choose font...");
fontItem.addActionListener(this);
optionsMenu.add(fontItem); fontDlg = new FontDialog(this); // Create the font dialog createToolbar();
toolBar.setRollover(true); JMenu helpMenu = new JMenu("Help"); // Create Help menu
helpMenu.setMnemonic('H'); // Create Help shortcut // Add the About item to the Help menu
aboutItem = new JMenuItem("About"); // Create About menu item
aboutItem.addActionListener(this); // Listener is the frame
helpMenu.add(aboutItem); // Add item to menu
menuBar.add(helpMenu); // Add Help menu to menu bar getContentPane().add(toolBar, BorderLayout.NORTH); // Add the toolbar
getContentPane().add(statusBar, BorderLayout.SOUTH); // Add the statusbar
} // Create File menu item actions
private void createFileMenuActions() {
newAction = new FileAction("New", 'N', CTRL_DOWN_MASK);
openAction = new FileAction("Open", 'O', CTRL_DOWN_MASK);
closeAction = new FileAction("Close");
saveAction = new FileAction("Save", 'S', CTRL_DOWN_MASK);
saveAsAction = new FileAction("Save As...");
printAction = new FileAction("Print", 'P', CTRL_DOWN_MASK);
exitAction = new FileAction("Exit", 'X', CTRL_DOWN_MASK); // Initialize the array
FileAction[] actions = {openAction, closeAction, saveAction, saveAsAction, printAction, exitAction};
fileActions = actions; // Add toolbar icons
newAction.putValue(LARGE_ICON_KEY, NEW24);
openAction.putValue(LARGE_ICON_KEY, OPEN24);
saveAction.putValue(LARGE_ICON_KEY, SAVE24);
saveAsAction.putValue(LARGE_ICON_KEY, SAVEAS24);
printAction.putValue(LARGE_ICON_KEY, PRINT24); // Add menu item icons
newAction.putValue(SMALL_ICON, NEW16);
openAction.putValue(SMALL_ICON, OPEN16);
saveAction.putValue(SMALL_ICON, SAVE16);
saveAsAction.putValue(SMALL_ICON,SAVEAS16);
printAction.putValue(SMALL_ICON, PRINT16); // Add tooltip text
newAction.putValue(SHORT_DESCRIPTION, "Create a new sketch");
openAction.putValue(SHORT_DESCRIPTION, "Read a sketch from a file");
closeAction.putValue(SHORT_DESCRIPTION, "Close the current sketch");
saveAction.putValue(SHORT_DESCRIPTION, "Save the current sketch to file");
saveAsAction.putValue(SHORT_DESCRIPTION, "Save the current sketch to a new file");
printAction.putValue(SHORT_DESCRIPTION, "Print the current sketch");
exitAction.putValue(SHORT_DESCRIPTION, "Exit Sketcher");
} // Helper method to set custom icon color
private void setCustomIconColor(ImageIcon icon, Color color) {
BufferedImage image = (BufferedImage)(icon.getImage());
int width = image.getWidth(); // Image width
int indent = width == 16 ? 3 : 2; // Indent for filled rectangle
int rectSize = width - 2*indent; // Filled rectangle size
Graphics2D g2D = image.createGraphics();
g2D.setPaint(color);
g2D.fillRect(indent,indent,rectSize,rectSize); // Fill centered rectangle
if(width == 24) {
TextLayout textLayout = new TextLayout("C", g2D.getFont(), g2D.getFontRenderContext());
Rectangle2D.Float rect = (Rectangle2D.Float)textLayout.getBounds();
g2D.setPaint(new Color(255-color.getRed(),255-color.getGreen(), 255-color.getBlue()));
g2D.drawString("C", (width-rect.width)/2, (width+rect.height)/2);
}
g2D.dispose();
} // Create the File menu
private void createFileMenu() {
JMenu fileMenu = new JMenu("File"); // Create File menu
fileMenu.setMnemonic('F'); // Create shortcut
createFileMenuActions(); // Create Actions for File menu item // Construct the file drop-down menu
fileMenu.add(newAction); // New Sketch menu item
fileMenu.add(openAction); // Open sketch menu item
fileMenu.add(closeAction); // Close sketch menu item
fileMenu.addSeparator(); // Add separator
fileMenu.add(saveAction); // Save sketch to file
fileMenu.add(saveAsAction); // Save As menu item
fileMenu.addSeparator(); // Add separator
fileMenu.add(printAction); // Print sketch menu item
fileMenu.addSeparator(); // Add separator
fileMenu.add(exitAction); // Print sketch menu item
menuBar.add(fileMenu); // Add the file menu
} // Create Element menu actions
private void createElementTypeActions() {
lineAction = new TypeAction("Line", LINE, 'L', CTRL_DOWN_MASK);
rectangleAction = new TypeAction("Rectangle", RECTANGLE, 'R', CTRL_DOWN_MASK);
circleAction = new TypeAction("Circle", CIRCLE,'C', CTRL_DOWN_MASK);
curveAction = new TypeAction("Curve", CURVE,'U', CTRL_DOWN_MASK);
textAction = new TypeAction("Text", TEXT,'T', CTRL_DOWN_MASK); // Initialize the array
TypeAction[] actions = {lineAction, rectangleAction, circleAction, curveAction, textAction};
typeActions = actions; // Add toolbar icons
lineAction.putValue(LARGE_ICON_KEY, LINE24);
rectangleAction.putValue(LARGE_ICON_KEY, RECTANGLE24);
circleAction.putValue(LARGE_ICON_KEY, CIRCLE24);
curveAction.putValue(LARGE_ICON_KEY, CURVE24);
textAction.putValue(LARGE_ICON_KEY, TEXT24); // Add menu item icons
lineAction.putValue(SMALL_ICON, LINE16);
rectangleAction.putValue(SMALL_ICON, RECTANGLE16);
circleAction.putValue(SMALL_ICON, CIRCLE16);
curveAction.putValue(SMALL_ICON, CURVE16);
textAction.putValue(SMALL_ICON, TEXT16); // Add tooltip text
lineAction.putValue(SHORT_DESCRIPTION, "Draw lines");
rectangleAction.putValue(SHORT_DESCRIPTION, "Draw rectangles");
circleAction.putValue(SHORT_DESCRIPTION, "Draw circles");
curveAction.putValue(SHORT_DESCRIPTION, "Draw curves");
textAction.putValue(SHORT_DESCRIPTION, "Draw text");
} // Create the Elements menu
private void createElementMenu() {
createElementTypeActions();
elementMenu = new JMenu("Elements"); // Create Elements menu
elementMenu.setMnemonic('E'); // Create shortcut
createRadioButtonDropDown(elementMenu, typeActions, lineAction);
menuBar.add(elementMenu); // Add the element menu
} // Create Color menu actions
private void createElementColorActions() {
redAction = new ColorAction("Red", RED, 'R', CTRL_DOWN_MASK|ALT_DOWN_MASK);
yellowAction = new ColorAction("Yellow", YELLOW, 'Y', CTRL_DOWN_MASK|ALT_DOWN_MASK);
greenAction = new ColorAction("Green", GREEN, 'G', CTRL_DOWN_MASK|ALT_DOWN_MASK);
blueAction = new ColorAction("Blue", BLUE, 'B', CTRL_DOWN_MASK|ALT_DOWN_MASK);
customAction = new ColorAction("Custom...", BLUE, 'C', CTRL_DOWN_MASK|ALT_DOWN_MASK); // Initialize the array
ColorAction[] actions = {redAction, greenAction, blueAction, yellowAction, customAction};
colorActions = actions; // Add toolbar icons
redAction.putValue(LARGE_ICON_KEY, RED24);
greenAction.putValue(LARGE_ICON_KEY, GREEN24);
blueAction.putValue(LARGE_ICON_KEY, BLUE24);
yellowAction.putValue(LARGE_ICON_KEY, YELLOW24);
customAction.putValue(LARGE_ICON_KEY, CUSTOM24); // Add menu item icons
redAction.putValue(SMALL_ICON, RED16);
greenAction.putValue(SMALL_ICON, GREEN16);
blueAction.putValue(SMALL_ICON, BLUE16);
yellowAction.putValue(SMALL_ICON, YELLOW16);
customAction.putValue(SMALL_ICON, CUSTOM16); // Add tooltip text
redAction.putValue(SHORT_DESCRIPTION, "Draw in red");
greenAction.putValue(SHORT_DESCRIPTION, "Draw in green");
blueAction.putValue(SHORT_DESCRIPTION, "Draw in blue");
yellowAction.putValue(SHORT_DESCRIPTION, "Draw in yellow");
customAction.putValue(SHORT_DESCRIPTION, "Draw in custom color");
} // Create the Color menu
private void createColorMenu() {
createElementColorActions();
colorMenu = new JMenu("Color"); // Create Elements menu
colorMenu.setMnemonic('C'); // Create shortcut
createRadioButtonDropDown(colorMenu, colorActions, blueAction);
menuBar.add(colorMenu); // Add the color menu
} // Menu creation helper
private void createRadioButtonDropDown(JMenu menu, Action[] actions, Action selected) {
ButtonGroup group = new ButtonGroup();
JRadioButtonMenuItem item = null;
for(Action action : actions) {
group.add(menu.add(item = new JRadioButtonMenuItem(action)));
if(action == selected) {
item.setSelected(true); // This is default selected
}
}
} // Create pop-up menu
private void createPopupMenu() {
// Element menu items
popup.add(new JMenuItem(lineAction));
popup.add(new JMenuItem(rectangleAction));
popup.add(new JMenuItem(circleAction));
popup.add(new JMenuItem(curveAction));
popup.add(new JMenuItem(textAction)); popup.addSeparator(); // Color menu items
popup.add(new JMenuItem(redAction));
popup.add(new JMenuItem(yellowAction));
popup.add(new JMenuItem(greenAction));
popup.add(new JMenuItem(blueAction));
popup.add(new JMenuItem(customAction));
} // Create toolbar buttons on the toolbar
private void createToolbar() {
for(FileAction action: fileActions){
if(action != exitAction && action != closeAction)
addToolbarButton(action); // Add the toolbar button
}
toolBar.addSeparator(); // Create Color menu buttons
for(ColorAction action:colorActions){
addToolbarButton(action); // Add the toolbar button
} toolBar.addSeparator(); // Create Elements menu buttons
for(TypeAction action:typeActions){
addToolbarButton(action); // Add the toolbar button
}
} // Create and add a toolbar button
private void addToolbarButton(Action action) {
JButton button = new JButton(action); // Create from Action
button.setBorder(BorderFactory.createCompoundBorder( // Add button border
new EmptyBorder(2,5,5,2), // Outside border
BorderFactory.createRaisedBevelBorder())); // Inside border
button.setHideActionText(true); // No label on the button
toolBar.add(button); // Add the toolbar button
} // Return the current drawing color
public Color getElementColor() {
return elementColor;
} // Return the current element type
public int getElementType() {
return elementType;
} // Return current text font
public Font getFont() {
return textFont;
} // Method to set the current font
public void setFont(Font font) {
textFont = font;
} // Retrieve the pop-up menu
public JPopupMenu getPopup() {
return popup;
} // Set radio button menu checks
private void setChecks(JMenu menu, Object eventSource) {
if(eventSource instanceof JButton){
JButton button = (JButton)eventSource;
Action action = button.getAction();
for(int i = 0 ; i<menu.getItemCount() ; ++i) {
JMenuItem item = menu.getItem(i);
item.setSelected(item.getAction() == action);
}
}
} // Handle About menu events
public void actionPerformed(ActionEvent e) {
if(e.getSource() == aboutItem) {
// Create about dialog with the app window as parent
JOptionPane.showMessageDialog(this, // Parent
"Sketcher Copyright Ivor Horton 2011", // Message
"About Sketcher", // Title
JOptionPane.INFORMATION_MESSAGE); // Message type
} else if(e.getSource() == fontItem) { // Set the dialog window position
fontDlg.setLocationRelativeTo(this);
fontDlg.setVisible(true); // Show the dialog
}
} // Inner class defining Action objects for File menu items
class FileAction extends AbstractAction {
// Create action with a name
FileAction(String name) {
super(name);
} // Create action with a name and accelerator
FileAction(String name, char ch, int modifiers) {
super(name);
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(ch, modifiers)); // Now find the character to underline
int index = name.toUpperCase().indexOf(ch);
if(index != -1) {
putValue(DISPLAYED_MNEMONIC_INDEX_KEY, index);
}
} // Event handler
public void actionPerformed(ActionEvent e) {
// You will add action code here eventually...
}
} // Inner class defining Action objects for Element type menu items
class TypeAction extends AbstractAction {
// Create action with just a name property
TypeAction(String name, int typeID) {
super(name);
this.typeID = typeID;
} // Create action with a name and an accelerator
private TypeAction(String name,int typeID, char ch, int modifiers) {
this(name, typeID);
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(ch, modifiers)); // Now find the character to underline
int index = name.toUpperCase().indexOf(ch);
if(index != -1) {
putValue(DISPLAYED_MNEMONIC_INDEX_KEY, index);
}
} public void actionPerformed(ActionEvent e) {
elementType = typeID;
setChecks(elementMenu, e.getSource());
statusBar.setTypePane(typeID);
} private int typeID;
} // Handles color menu items
class ColorAction extends AbstractAction {
// Create an action with a name and a color
public ColorAction(String name, Color color) {
super(name);
this.color = color;
} // Create an action with a name, a color, and an accelerator
public ColorAction(String name, Color color, char ch, int modifiers) {
this(name, color);
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(ch, modifiers)); // Now find the character to underline
int index = name.toUpperCase().indexOf(ch);
if(index != -1) {
putValue(DISPLAYED_MNEMONIC_INDEX_KEY, index);
}
} public void actionPerformed(ActionEvent e) {
if(this == customAction) {
// This could be a new custom color
Color newColor = JColorChooser.showDialog(SketcherFrame.this, "Select Custom Color", customColor);
if(newColor != null) {
elementColor = customColor = newColor; // Setup small custom color icons
setCustomIconColor(CUSTOM16,customColor);
setCustomIconColor(CUSTOM24,customColor);
toolBar.repaint(); // Repaint the toolbar
}
} else {
// This is just a standard color change
elementColor = color;
}
statusBar.setColorPane(elementColor); // Update the status bar
setChecks(colorMenu, e.getSource()); // Set Color menu checks
} private Color color;
} // File actions
private FileAction newAction, openAction, closeAction, saveAction, saveAsAction, printAction, exitAction;
private FileAction[] fileActions; // File actions as an array // Element type actions
private TypeAction lineAction, rectangleAction, circleAction, curveAction, textAction;
private TypeAction[] typeActions; // Type actions as an array // Element color actions
private ColorAction redAction, yellowAction,greenAction, blueAction, customAction;
private ColorAction[] colorActions; // Color actions as an array private JMenuBar menuBar = new JMenuBar(); // Window menu bar
private JMenu elementMenu; // Elements menu
private JMenu colorMenu; // Color menu
private JMenu optionsMenu; // Options menu private StatusBar statusBar = new StatusBar(); // Window status bar
private FontDialog fontDlg; // The font dialog private JMenuItem aboutItem; // About menu item
private JMenuItem fontItem; // Font chooser menu item private JPopupMenu popup = new JPopupMenu("General"); // Window pop-up
private Color elementColor = DEFAULT_ELEMENT_COLOR; // Current element color
private Color customColor = DEFAULT_ELEMENT_COLOR; // Current custom color
private int elementType = DEFAULT_ELEMENT_TYPE; // Current element type
private Font textFont = DEFAULT_FONT; // Default font for text elements
private JToolBar toolBar = new JToolBar(); // Window toolbar
private Sketcher theApp; // The application object
}

3、元素类:

 import java.awt.*;
import java.io.Serializable;
import static Constants.SketcherConstants.*;
import java.awt.geom.*; public abstract class Element implements Serializable{ public Element(Point position, Color color) {
this.position = new Point(position);
this.color = color;
} protected Element(Color color) {
this.color = color;
} // Returns the color of the element
public Color getColor() {
return color;
} // Returns the position of the element
public Point getPosition() {
return position;
} // Returns the bounding rectangle enclosing an element boundary
public java.awt.Rectangle getBounds() {
AffineTransform at = AffineTransform.getTranslateInstance(position.x, position.y);
at.rotate(angle);
at.translate(-position.x, -position.y);
return at.createTransformedShape(bounds).getBounds();
} // Set or reset highlight color
public void setHighlighted(boolean highlighted) {
this.highlighted = highlighted;
} // Create a new element
public static Element createElement(int type, Color color, Point start, Point end) {
switch(type) {
case LINE:
return new Element.Line(start, end, color);
case RECTANGLE:
return new Rectangle(start, end, color);
case CIRCLE:
return new Circle(start, end, color);
case CURVE:
return new Curve(start, end, color);
default:
assert false; // We should never get to here
}
return null;
} // Draw a geometric element of type Shape
protected void draw(Graphics2D g2D, Shape element) {
g2D.setPaint(highlighted ? Color.MAGENTA : color); // Set the element color
AffineTransform old = g2D.getTransform(); // Save copy of current transform
g2D.translate((double)position.x, (double)position.y); // Add a translation to current transform
g2D.rotate(angle); // Rotate about position
g2D.draw(element); // Draw the element
g2D.setTransform(old); // Restore original transform
} // Move an element
public void move(int deltaX, int deltaY) {
position.translate(deltaX, deltaY);
bounds.translate(deltaX, deltaY);
} // Rotate an element
public void setRotation(double angle) {
this.angle = angle;
} // Get the rotation angle
public double getRotation() {
return angle;
} // Nested class defining a line
public static class Line extends Element {
public Line(Point start, Point end, Color color) {
super(start, color);
line = new Line2D.Double(origin.x, origin.y, end.x - position.x, end.y - position.y);
bounds = new java.awt.Rectangle(
Math.min(start.x ,end.x), Math.min(start.y, end.y),
Math.abs(start.x - end.x)+1, Math.abs(start.y - end.y)+1);
} // Change the end point for the line
public void modify(Point start, Point last) {
line.x2 = last.x - position.x;
line.y2 = last.y - position.y;
bounds = new java.awt.Rectangle(
Math.min(start.x ,last.x), Math.min(start.y, last.y),
Math.abs(start.x - last.x)+1, Math.abs(start.y - last.y)+1);
} // Display the line
public void draw(Graphics2D g2D) {
draw(g2D, line); // Draw the line
}
private Line2D.Double line;
private final static long serialVersionUID = 1001L;
} // Nested class defining a rectangle
public static class Rectangle extends Element {
public Rectangle(Point start, Point end, Color color) {
super(new Point(Math.min(start.x, end.x), Math.min(start.y, end.y)), color);
rectangle = new Rectangle2D.Double(
origin.x, origin.y, // Top-left corner
Math.abs(start.x - end.x), Math.abs(start.y - end.y)); // Width & height
bounds = new java.awt.Rectangle(
Math.min(start.x ,end.x), Math.min(start.y, end.y),
Math.abs(start.x - end.x)+1, Math.abs(start.y - end.y)+1);
} // Display the rectangle
public void draw(Graphics2D g2D) {
draw(g2D, rectangle); // Draw the rectangle
} // Method to redefine the rectangle
public void modify(Point start, Point last) {
bounds.x = position.x = Math.min(start.x, last.x);
bounds.y = position.y = Math.min(start.y, last.y);
rectangle.width = Math.abs(start.x - last.x);
rectangle.height = Math.abs(start.y - last.y);
bounds.width = (int)rectangle.width + 1;
bounds.height = (int)rectangle.height + 1;
} private Rectangle2D.Double rectangle;
private final static long serialVersionUID = 1001L;
} // Nested class defining a circle
public static class Circle extends Element {
public Circle(Point center, Point circum, Color color) {
super(color); // Radius is distance from center to circumference
double radius = center.distance(circum);
position = new Point(center.x - (int)radius, center.y - (int)radius);
circle = new Ellipse2D.Double(origin.x, origin.y, 2.*radius, 2.*radius);
bounds = new java.awt.Rectangle(position.x, position.y,
1 + (int)circle.width, 1 + (int)circle.height);
} // Display the circle
public void draw(Graphics2D g2D) {
draw(g2D, circle); // Draw the circle
} // Recreate this circle
public void modify(Point center, Point circum) {
double radius = center.distance(circum);
circle.width = circle.height = 2*radius;
position.x = center.x - (int)radius;
position.y = center.y - (int)radius;
bounds = new java.awt.Rectangle(position.x, position.y,
1 + (int)circle.width, 1 + (int)circle.height);
} private Ellipse2D.Double circle;
private final static long serialVersionUID = 1001L;
} // Nested class defining a curve
public static class Curve extends Element {
public Curve(Point start, Point next, Color color) {
super(start, color);
curve = new GeneralPath();
curve.moveTo(origin.x, origin.y); // Set current position as origin
curve.lineTo(next.x - position.x, next.y - position.y); // Add segment
bounds = new java.awt.Rectangle(
Math.min(start.x, next.x), Math.min(start.y, next.y),
Math.abs(next.x - start.x)+1, Math.abs(next.y - start.y)+1);
} // Add another segment
public void modify(Point start, Point next) {
curve.lineTo(next.x - position.x, next.y - position.y); // Add segment
bounds.add(new java.awt.Rectangle(next.x,next.y, 1, 1)); // Extend bounds
} // Display the curve
public void draw(Graphics2D g2D) {
draw(g2D, curve); // Draw the curve
} private GeneralPath curve;
private final static long serialVersionUID = 1001L;
} // Nested class defining a Text element
public static class Text extends Element {
public Text(String text, Point start, Color color, FontMetrics fm) {
super(start, color);
this.text = text;
this.font = fm.getFont();
maxAscent = fm.getMaxAscent();
bounds = new java.awt.Rectangle(position.x, position.y,
fm.stringWidth(text) + 4, maxAscent+ fm.getMaxDescent() + 4);
} public void draw(Graphics2D g2D) {
g2D.setPaint(highlighted ? HIGHLIGHT_COLOR : color);
Font oldFont = g2D.getFont(); // Save the old font
g2D.setFont(font); // Set the new font AffineTransform old = g2D.getTransform(); // Save the current transform
g2D.translate((double)position.x, (double)position.y); // Add translation transform to current
g2D.rotate(angle); // Rotate about position
// Reference point for drawString() is the baseline of the 1st character
g2D.drawString(text, origin.x + 2, maxAscent + 2);
g2D.setTransform(old); // Restore original transform
g2D.setFont(oldFont); // Restore the old font
} public void modify(Point start, Point last) {
// No code is required here, but you must supply a definition
} private Font font; // The font to be used
private int maxAscent; // Maximum ascent
private String text; // Text to be displayed
private final static long serialVersionUID = 1001L;
} // Abstract Element class methods
public abstract void draw(Graphics2D g2D);
public abstract void modify(Point start, Point last); // Element class fields
protected Point position; // Position of a shape
protected Color color; // Color of a shape
protected java.awt.Rectangle bounds; // Bounding rectangle
protected static final Point origin = new Point(); // Origin for elements
protected boolean highlighted = false; // Highlight flag
protected double angle = 0.0; // Element rotation
private final static long serialVersionUID = 1001L;
}

4、模型类:

 import java.io.Serializable;
import java.util.*; public class SketcherModel extends Observable implements Serializable, Iterable<Element> { //Remove an element from the sketch
public boolean remove(Element element) {
boolean removed = elements.remove(element);
if(removed) {
setChanged();
notifyObservers(element.getBounds());
}
return removed;
} //Add an element to the sketch
public void add(Element element) {
elements.add(element);
setChanged();
notifyObservers(element.getBounds());
} // Get iterator for sketch elements
public Iterator<Element> iterator() {
return elements.iterator();
} protected LinkedList<Element> elements = new LinkedList<>();
private final static long serialVersionUID = 1001L;
}

5、字体对话框类:

 // Class to define a dialog to choose a font
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.event.*;
import static Constants.SketcherConstants.*; @SuppressWarnings("serial")
class FontDialog extends JDialog
implements ActionListener, // For buttons etc.
ListSelectionListener, // For list box
ChangeListener { // For the spinner
// Constructor
public FontDialog(SketcherFrame window) {
// Call the base constructor to create a modal dialog
super(window, "Font Selection", true);
font = window.getFont(); // Get the current font // Create the dialog button panel
JPanel buttonPane = new JPanel(); // Create a panel to hold buttons // Create and add the buttons to the buttonPane
buttonPane.add(ok = createButton("OK")); // Add the OK button
buttonPane.add(cancel = createButton("Cancel")); // Add the Cancel button
getContentPane().add(buttonPane, BorderLayout.SOUTH); // Add pane // Code to create the data input panel
JPanel dataPane = new JPanel(); // Create the data entry panel
dataPane.setBorder(BorderFactory.createCompoundBorder( // Pane border
BorderFactory.createLineBorder(Color.BLACK),
BorderFactory.createEmptyBorder(5, 5, 5, 5)));
GridBagLayout gbLayout = new GridBagLayout(); // Create the layout
dataPane.setLayout(gbLayout); // Set the pane layout
GridBagConstraints constraints = new GridBagConstraints(); // Create the font choice and add it to the input panel
JLabel label = new JLabel("Choose a Font");
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridwidth = GridBagConstraints.REMAINDER;
gbLayout.setConstraints(label, constraints);
dataPane.add(label); // Code to set up font list choice component
GraphicsEnvironment e = GraphicsEnvironment.getLocalGraphicsEnvironment();
String[] fontNames = e.getAvailableFontFamilyNames(); // Get font names fontList = new JList<>(fontNames); // Create list of font names
fontList.setValueIsAdjusting(true); // single event selection
fontList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
fontList.setSelectedValue(font.getFontName(),true);
fontList.addListSelectionListener(this);
fontList.setToolTipText("Choose a font");
JScrollPane chooseFont = new JScrollPane(fontList); // Scrollable list
chooseFont.setMinimumSize(new Dimension(400,100));
chooseFont.setWheelScrollingEnabled(true); // Enable mouse wheel scroll // Panel to display font sample
JPanel display = new JPanel(true);
fontDisplay = new JLabel("Sample Size: x X y Y z Z");
fontDisplay.setFont(font);
fontDisplay.setPreferredSize(new Dimension(350,100));
display.add(fontDisplay); //Create a split pane with font choice at the top and font display at the bottom
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
true,
chooseFont,
display);
gbLayout.setConstraints(splitPane, constraints); // Split pane constraints
dataPane.add(splitPane); // Add to the data pane // Set up the size choice using a spinner
JPanel sizePane = new JPanel(true); // Pane for size choices
label = new JLabel("Choose point size: "); // Prompt for point size
sizePane.add(label); // Add the prompt chooseSize = new JSpinner(new SpinnerNumberModel(font.getSize(),
POINT_SIZE_MIN, POINT_SIZE_MAX, POINT_SIZE_STEP));
chooseSize.setValue(font.getSize()); // Set current font size
chooseSize.addChangeListener(this);
sizePane.add(chooseSize); // Add spinner to pane
gbLayout.setConstraints(sizePane, constraints); // Set pane constraints
dataPane.add(sizePane); // Add the pane // Set up style options using radio buttons
bold = new JRadioButton("Bold", (font.getStyle() & Font.BOLD) > 0);
italic = new JRadioButton("Italic", (font.getStyle() & Font.ITALIC) > 0);
bold.addItemListener(new StyleListener(Font.BOLD)); // Add button listeners
italic.addItemListener(new StyleListener(Font.ITALIC));
JPanel stylePane = new JPanel(true); // Create style pane
stylePane.add(bold); // Add buttons
stylePane.add(italic); // to style pane...
gbLayout.setConstraints(stylePane, constraints); // Set pane constraints
dataPane.add(stylePane); // Add the pane getContentPane().add(dataPane, BorderLayout.CENTER);
pack();
setVisible(false);
} // Create a dialog button
JButton createButton(String label) {
JButton button = new JButton(label); // Create the button
button.setPreferredSize(new Dimension(80,20)); // Set the size
button.addActionListener(this); // Listener is the dialog
return button; // Return the button
} // Handler for button events
public void actionPerformed(ActionEvent e) {
if(e.getSource()== ok) { // If it's the OK button
((SketcherFrame)getOwner()).setFont(font); // ...set selected font
} else {
font = ((SketcherFrame)getOwner()).getFont(); // Restore the current font
fontDisplay.setFont(font);
chooseSize.setValue(font.getSize()); // Restore the point size
fontList.setSelectedValue(font.getName(),true);
int style = font.getStyle();
bold.setSelected((style & Font.BOLD) > 0); // Restore the
italic.setSelected((style & Font.ITALIC) > 0); // style options
}
// Now hide the dialog - for ok or cancel
setVisible(false);
} // List selection listener method
public void valueChanged(ListSelectionEvent e) {
if(!e.getValueIsAdjusting()) {
font = new Font(fontList.getSelectedValue(), font.getStyle(), font.getSize());
fontDisplay.setFont(font);
fontDisplay.repaint();
}
} // Handle spinner state change events
public void stateChanged(ChangeEvent e) {
int fontSize = ((Number)(((JSpinner)e.getSource()).getValue())).intValue();
font = font.deriveFont((float)fontSize);
fontDisplay.setFont(font);
fontDisplay.repaint();
} // Iner class defining listeners for radio buttons
class StyleListener implements ItemListener {
public StyleListener(int style) {
this.style = style;
} // Event handler for font style changes
public void itemStateChanged(ItemEvent e) {
int fontStyle = font.getStyle();
if(e.getStateChange()==ItemEvent.SELECTED) { // If style was selected
fontStyle |= style; // turn it on in the font style
} else {
fontStyle &= ~style; // otherwise turn it off
}
font = font.deriveFont(fontStyle); // Get a new font
fontDisplay.setFont(font); // Change the label font
fontDisplay.repaint(); // repaint
}
private int style; // Style for this listener
} private JList<String> fontList; // Font list
private JButton ok; // OK button
private JButton cancel; // Cancel button
private JRadioButton bold; // Bold style button
private JRadioButton italic; // Italic style button
private Font font; // Currently selected font
private JSpinner chooseSize; // Font size options
private JLabel fontDisplay; // Font sample
}

6、状态栏类:

 // Class defining a status bar
import javax.swing.*;
import java.awt.*;
import javax.swing.border.BevelBorder;
import static Constants.SketcherConstants.*; @SuppressWarnings("serial")
class StatusBar extends JPanel {
// Constructor
public StatusBar() {
setLayout(new FlowLayout(FlowLayout.LEFT, 10, 3));
setBackground(Color.LIGHT_GRAY);
setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));
setColorPane(DEFAULT_ELEMENT_COLOR);
setTypePane(DEFAULT_ELEMENT_TYPE);
add(colorPane); // Add color pane to status bar
add(typePane); // Add type pane to status bar
} // Set color pane contents
public void setColorPane(Color color) {
String text = null; // Text for the color pane
Icon icon = null; // Icon to be displayed
if(color.equals(Color.RED)) {
text = "RED";
icon = RED16;
} else if(color.equals(Color.YELLOW)) {
text = "YELLOW";
icon = YELLOW16;
} else if(color.equals(Color.GREEN)) {
text = "GREEN";
icon = GREEN16;
} else if(color.equals(Color.BLUE)) {
text = "BLUE";
icon = BLUE16;
} else {
text = "CUSTOM";
icon = CUSTOM16;
}
colorPane.setIcon(icon);
colorPane.setText(text); // Set the pane text
} // Set type pane label
public void setTypePane(int elementType) {
String text = null; // Text for the type pane
switch(elementType) {
case LINE:
text = "LINE";
break;
case RECTANGLE:
text = "RECTANGLE";
break;
case CIRCLE:
text = "CIRCLE";
break;
case CURVE:
text = "CURVE";
break;
case TEXT:
text = "TEXT";
break;
default:
assert false;
}
typePane.setText(text); // Set the pane text
} // Panes in the status bar
private StatusPane colorPane = new StatusPane("BLUE", BLUE16);;
private StatusPane typePane = new StatusPane("LINE"); // Class defining a status bar pane
class StatusPane extends JLabel {
// Constructor - text only
public StatusPane(String text) {
super(text, LEFT);
setupPane();
} // Constructor - text with an icon
public StatusPane(String text, Icon icon) {
super(text, icon, LEFT);
setupPane();
} // Helper method for use by constructors
private void setupPane() {
setBackground(Color.LIGHT_GRAY); // Set background color
setForeground(Color.BLACK); // Set foreground color
setFont(paneFont); // Set the fixed font
setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createBevelBorder(BevelBorder.LOWERED), // Outside border
BorderFactory.createEmptyBorder(0,5,0,3))); // Inside border
setPreferredSize(new Dimension(80,20));
} // Font for pane text
private Font paneFont = new Font("Serif", Font.PLAIN, 10);
}
}

7、视图类:

 import javax.swing.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.event.MouseInputAdapter;
import static Constants.SketcherConstants.*;
import java.awt.geom.Line2D; @SuppressWarnings("serial")
public class SketcherView extends JComponent implements Observer {
public SketcherView(Sketcher theApp) {
this.theApp = theApp;
MouseHandler handler = new MouseHandler(); // create the mouse listener
addMouseListener(handler); // Listen for button events
addMouseMotionListener(handler); // Listen for motion events // Add the pop-up menu items
JMenuItem moveItem = elementPopup.add(new JMenuItem("Move"));
JMenuItem deleteItem = elementPopup.add(new JMenuItem("Delete"));
JMenuItem rotateItem = elementPopup.add(new JMenuItem("Rotate"));
JMenuItem sendToBackItem = elementPopup.add(new JMenuItem("Send-to-back")); // Create the menu item listeners
moveItem.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
mode = MOVE;
selectedElement = highlightElement;
}
});
deleteItem.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
deleteElement();
}
});
rotateItem.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
mode = ROTATE;
selectedElement = highlightElement;
}
});
sendToBackItem.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
sendToBack(); // Handle the send-to-back event
}
});
} // Send-to Back operation
private void sendToBack() {
if(highlightElement != null) {
SketcherModel sketch = theApp.getModel();
if(sketch.remove(highlightElement)) {
sketch.add(highlightElement);
} else {
JOptionPane.showMessageDialog(
SketcherView.this,"Element not found to remove.",
"Remove Element from Sketch", JOptionPane.ERROR_MESSAGE);
}
}
} // Delete element operation
private void deleteElement() {
if(highlightElement != null) {
if(!theApp.getModel().remove(highlightElement)) {
JOptionPane.showMessageDialog(
SketcherView.this,"Element not found to remove.",
"Remove Element from Sketch", JOptionPane.ERROR_MESSAGE);
}
highlightElement = null;
}
} // Method called by Observable object when it changes
public void update(Observable o, Object rectangle) {
if(rectangle != null) {
repaint((java.awt.Rectangle)rectangle);
} else {
repaint();
}
} // Method to draw on the view
@Override
public void paint(Graphics g) {
Graphics2D g2D = (Graphics2D)g; // Get a 2D device context
for(Element element: theApp.getModel()) { // For each element in the model
element.draw(g2D); // ...draw the element
}
} class MouseHandler extends MouseInputAdapter {
@Override
public void mousePressed(MouseEvent e) {
start = e.getPoint(); // Save the cursor position in start
buttonState = e.getButton(); // Record which button was pressed if(showContextMenu(e)) {
start = null;
buttonState = MouseEvent.NOBUTTON;
return;
} if(theApp.getWindow().getElementType() == TEXT && mode == NORMAL) return; // Initialize rotation angles when mode is ROTATE
if(mode == ROTATE && selectedElement != null) {
oldAngle = selectedElement.getRotation();
angle = 0.0;
} if(buttonState == MouseEvent.BUTTON1 && mode == NORMAL) {
g2D = (Graphics2D)getGraphics(); // Get graphics context
g2D.setXORMode(getBackground()); // Set XOR mode
}
} // Show the context menu when an element is highlighted
private boolean showContextMenu(MouseEvent e) {
if(e.isPopupTrigger()){
if(last != null) { // If mouse was dragged
start = last; // show popup at last cursor position
}
if(highlightElement == null) { // If there is no highlighted element
// Show the popup menu from the app window
theApp.getWindow().getPopup().show(SketcherView.this, start.x, start.y);
} else { // Otherwise...
// Show the element operations context menu
elementPopup.show(SketcherView.this, start.x, start.y);
}
return true;
}
return false;
} @Override
public void mouseDragged(MouseEvent e) {
last = e.getPoint(); // Save cursor position
if(theApp.getWindow().getElementType() == TEXT && mode == NORMAL) return; // Select operation based on sketching mode
switch(mode){
case NORMAL:
// Creating an element
if(buttonState == MouseEvent.BUTTON1) {
if(tempElement == null) { // Is there an element?
tempElement = Element.createElement( // No, so create one
theApp.getWindow().getElementType(),
theApp.getWindow().getElementColor(),
start, last);
} else {
tempElement.draw(g2D); // Yes draw to erase it
tempElement.modify(start, last); // Now modify it
}
tempElement.draw(g2D); // and draw it
}
break;
case MOVE:
// Moving an element
if(buttonState == MouseEvent.BUTTON1 && selectedElement != null) {
selectedElement.move(last.x-start.x, last.y-start.y); // Move it
repaint();
start = last; // Make start current point
}
break;
case ROTATE:
// Rotating an element
if(buttonState == MouseEvent.BUTTON1 && selectedElement != null) {
angle += getAngle(selectedElement.getPosition(), start, last);
if(angle != 0.0) { // If there is rotation
selectedElement.setRotation(oldAngle + angle); // Rotate the element
repaint(); // Repaint the view
start = last; // last is start next time
}
}
break;
}
} // Helper method for calculating the rotation angle
double getAngle(Point position, Point start, Point last) {
// Get perpendicular distance from last to line from position to start
double perp = Line2D.ptLineDist(position.x, position.y,
last.x, last.y, start.x, start.y); // Get the distance from position to start
double hypotenuse = position.distance(start); if(perp < 1.0 || hypotenuse < 1.0) return 0.0; // Ensure sensible values // Angle is the arc sine of perp/hypotenuse. Clockwise is positive angle
return -Line2D.relativeCCW(position.x, position.y, start.x, start.y,
last.x, last.y)*Math.asin(perp/hypotenuse);
} @Override
public void mouseReleased(MouseEvent e) {
if(mode == MOVE || mode == ROTATE) {
selectedElement = null;
start = last = null;
mode = NORMAL;
return;
} if(showContextMenu(e)) {
start = last = null;
return;
} // Check for TEXT being the element type
if(theApp.getWindow().getElementType() == TEXT && mode == NORMAL) {
if(last != null) {
start = last = null;
}
return;
} if(e.getButton() == MouseEvent.BUTTON1) {
buttonState = MouseEvent.NOBUTTON; // Reset the button state if(tempElement != null) { // If there is an element...
theApp.getModel().add(tempElement); // ...add it to the model...
tempElement = null; // ...and reset the field
}
if(g2D != null) { // If there抯 a graphics context
g2D.dispose(); // ...release the resource...
g2D = null; // ...and reset field to null
}
start = last = null; // Remove any points
mode = NORMAL; // Always normal after mouse released
}
} @Override
public void mouseClicked(MouseEvent e) {
// Only if it's TEXT and button 1 was clicked
if(theApp.getWindow().getElementType() == TEXT &&
buttonState == MouseEvent.BUTTON1) {
String text = JOptionPane.showInputDialog(
theApp.getWindow(),"Enter Input:",
"Create Text Element", JOptionPane.PLAIN_MESSAGE); if(text != null && !text.isEmpty()) { // Only if text was entered
g2D = (Graphics2D)getGraphics();
tempElement = new Element.Text(text,
start,
theApp.getWindow().getElementColor(),
g2D.getFontMetrics(theApp.getWindow().getFont()));
g2D.dispose();
g2D = null;
if(tempElement != null) {
theApp.getModel().add(tempElement);
}
}
tempElement = null; // Reset for next element creation
start = null; // Reset for next element
}
} // Handle mouse move events
@Override
public void mouseMoved(MouseEvent e) {
Point cursor = e.getPoint(); // Get current cursor position for(Element element : theApp.getModel()) { // Go through the list...
if(element.getBounds().contains(cursor)) { // ....under the cursor
if(element==highlightElement) { // If it's already highlighted
return; // we are done
} // Un-highlight any existing highlighted element
if(highlightElement!= null) { // If an element is highlighted
highlightElement.setHighlighted(false); // un-highlight it and
repaint(highlightElement.getBounds()); //... redraw it
} element.setHighlighted(true); // Set highlight for new element
highlightElement = element; // Store new highlighted element
repaint(highlightElement.getBounds());
return;
}
} // Here there is no element under the cursor so...
if(highlightElement!=null) { // If an element is highlighted...
highlightElement.setHighlighted(false); // ...turn off highlighting...
repaint(highlightElement.getBounds()); // ... and redraw the element
highlightElement = null; // No element highlighted
}
} @Override
public void mouseEntered(MouseEvent e) {
setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
} @Override
public void mouseExited(MouseEvent e) {
setCursor(Cursor.getDefaultCursor());
} private Point start; // Stores cursor position on press
private Point last; // Stores cursor position on drag
private Element tempElement = null; // Stores a temporary element
private int buttonState = MouseEvent.NOBUTTON; // Records button state
private Graphics2D g2D = null; // Temporary graphics context
} private Sketcher theApp; // The application object
private Element highlightElement; // Highlighted element
private Element selectedElement; // Element for move or rotate operation
private double oldAngle = 0.0; // Initial element rotation
private double angle = 0.0; // Additional rotation // Element operations context menu
private JPopupMenu elementPopup = new JPopupMenu("Element Operations"); private String mode = NORMAL; // Sketching mode
}

8、Sketcher类:

 // Sketching application
import javax.swing.*;
import java.awt.*;
import java.awt.event.*; public class Sketcher {
public static void main(String[] args) {
theApp = new Sketcher(); // Create the application object
SwingUtilities.invokeLater(new Runnable() {
public void run() {
theApp.createGUI(); // Call GUI creator
}
});
} // Method to create the application GUI
private void createGUI() {
window = new SketcherFrame("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 half screen size
window.setSize(wndSize.width/2, wndSize.height/2); // Set window size
window.setLocationRelativeTo(null); // Center window window.addWindowListener(new WindowHandler()); // Add window listener sketch = new SketcherModel(); // Create the model
view = new SketcherView(this); // Create the view
sketch.addObserver(view); // Register view with the model
window.getContentPane().add(view, BorderLayout.CENTER);
window.setVisible(true);
} // Return a reference to the application window
public SketcherFrame getWindow() {
return window;
} // Return a reference to the model
public SketcherModel getModel() {
return sketch;
} // Return a reference to the view
public SketcherView getView() {
return view;
} // Handler class for window events
class WindowHandler extends WindowAdapter {
// Handler for window closing event
@Override
public void windowClosing(WindowEvent e) {
// Code to be added here...
}
} private SketcherModel sketch; // The data model for the sketch
private SketcherView view; // The view of the sketch
private SketcherFrame window; // The application window
private static Sketcher theApp; // The application object
}
上一篇:javascript 数组和字符串的转化


下一篇:Android 实时文件夹