Java Notes
Example - PaintDemo
|
This simple paint applet / application leaves much to be desired, but it
illustrates a few things:
BufferedImage.
The standard Graphics class methods are used with two additions.
The BufferedImage object is used to accumulate the effect of all drawing.
The
Graphics2D. The Graphics2D class allows more extensive graphics operations
than the Graphics class. In this case it's needed to work with the BufferedImage.
Because Graphics2D is a child class (subclass) of
Graphics, it's easy to downcast the Graphics object that is passed
to |
Source code
The source code is dividing into three classes:
PaintDemo.java
builds the applet user interface and main program.PaintPanel.java
is a panel where the graphics are drawn and which listens to mouse events.Shape.java
is an enum class which defines the possible shapes. This is a "classic" enum style of programming, but Java also allows each enum constant to have a method associated with it. Alternative code using using this more sophisticated enum is shown in a section at the end.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
// File : gui-lowlevel/paintdemo/PaintDemo.java // Purpose: A simple painting program. // Illustrates use of mouse listeners and BufferedImage. // Runs as both applet and main program. // Author : Fred Swartz 2002, 2006-10-13 - Placed in public domain. // Possible Enhancements: // * Clear drawing area // * Other shapes. // * An eraser (define new shape, draw on buffered image during drag). package paintdemo; import java.awt.*; import java.awt.event.*; import javax.swing.*; ////////////////////////////////////////////////////////////////////// PaintDemo public class PaintDemo extends JApplet { //=================================================================== fields PaintPanel _canvas = new PaintPanel(); //===================================================================== main public static void main(String[] args) { //... Create and initialize the applet. JApplet theApplet = new PaintDemo(); //... Create a window (JFrame) and make applet the content pane. JFrame window = new JFrame(); window.setContentPane(theApplet); window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); window.setTitle("Paint Demo 2"); window.pack(); window.setLocationRelativeTo(null); // Center window. window.setResizable(false); // System.out.println(theApplet.getSize()); // to get applet size. window.setVisible(true); // Make the window visible. } //============================================================== constructor public PaintDemo() { //... Create radio buttons for shapes......................... JRadioButton circleButton = new JRadioButton("Oval"); circleButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { _canvas.setShape(Shape.OVAL); }}); JRadioButton rectangleButton = new JRadioButton("Rectangle", true); rectangleButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { _canvas.setShape(Shape.RECTANGLE); }}); JRadioButton lineButton = new JRadioButton("Line"); lineButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { _canvas.setShape(Shape.LINE); }}); ButtonGroup shapeGroup = new ButtonGroup(); shapeGroup.add(circleButton); shapeGroup.add(rectangleButton); shapeGroup.add(lineButton); //--- Layout the shape buttons JPanel shapePanel = new JPanel(); shapePanel.setLayout(new GridLayout(3,1)); shapePanel.add(circleButton); shapePanel.add(rectangleButton); shapePanel.add(lineButton); //... Create radio buttons for colors............................... JRadioButton redButton = new JRadioButton("Red", true); redButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { _canvas.setColor(Color.RED); }}); JRadioButton greenButton = new JRadioButton("Green"); greenButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { _canvas.setColor(Color.GREEN); }}); JRadioButton blueButton = new JRadioButton("Blue"); blueButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { _canvas.setColor(Color.BLUE); }}); ButtonGroup colorGroup = new ButtonGroup(); colorGroup.add(redButton); colorGroup.add(greenButton); colorGroup.add(blueButton); //... Layout the color buttons JPanel colorPanel = new JPanel(); colorPanel.setLayout(new GridLayout(3,1)); colorPanel.add(redButton); colorPanel.add(greenButton); colorPanel.add(blueButton); //... Create a panel to hold the separate button panels............. JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS)); buttonPanel.add(shapePanel); buttonPanel.add(colorPanel); buttonPanel.add(Box.createHorizontalGlue()); //... layout the applet ........................................... setLayout(new BorderLayout(5,5)); add(buttonPanel, BorderLayout.NORTH); add(_canvas , BorderLayout.CENTER); } } |
The graphics panel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
// File : gui-lowlevel/paintpanel/PaintPanel.java // Purpose: Draws graphics, handles mouse events. // Has setters for shape and color. // Author : Fred Swartz - October 12, 2006 - Placed in public domain. // Possible enhancements: // * Allow dragging shapes up or to left, not just down+right. // by using max/min of coordinates. package paintdemo; import java.awt.*; import java.awt.event.*; import java.awt.image.BufferedImage; import javax.swing.*; ///////////////////////////////////////////////////////////////////// PaintPanel class PaintPanel extends JPanel implements MouseListener, MouseMotionListener { //================================================================ constants private static final int SIZE = 300; // Size of paint area. private static final Shape INIIIAL_SHAPE = Shape.RECTANGLE; private static final Color INITIAL_COLOR = Color.RED; private static final Color COLOR_BACKGROUND = Color.WHITE; private enum State { IDLE, DRAGGING } //=================================================================== fields private State _state = State.IDLE; private Shape _shape = INIIIAL_SHAPE; private Color _color = INITIAL_COLOR; private Point _start = null; // Where mouse is pressed. private Point _end = null; // Where mouse is dragged to or released. //... BufferedImage stores the underlying saved painting. // Initialized first time paintComponent is called. private BufferedImage _bufImage = null; //============================================================== constructor public PaintPanel() { setPreferredSize(new Dimension(SIZE, SIZE)); setBackground(Color.white); this.addMouseListener(this); this.addMouseMotionListener(this); } //================================================================= setShape public void setShape(Shape shape) { _shape = shape; } //================================================================= setColor public void setColor(Color color) { _color = color; } //=========================================================== paintComponent @Override public void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D)g; // Downcast to Graphics2D //... One time initialization of in-memory, saved image. if (_bufImage == null) { //... This is the first time, initialize _bufImage int w = this.getWidth(); int h = this.getHeight(); _bufImage = (BufferedImage)this.createImage(w, h); Graphics2D gc = _bufImage.createGraphics(); gc.setColor(COLOR_BACKGROUND); gc.fillRect(0, 0, w, h); // fill in background } //... Display the saved image. g2.drawImage(_bufImage, null, 0, 0); //... Overwrite the screen display with currently dragged image. if (_state == State.DRAGGING) { //... Write shape that is being dragged over the screen image, // but not into the saved buffered image. It will be written // on the saved image when the mouse is released. drawCurrentShape(g2); } } //========================================================= drawCurrentShape private void drawCurrentShape(Graphics2D g2) { //... Draws current shape on a graphics context, either // on the context passed to paintComponent, or the // context for the BufferedImage. g2.setColor(_color); // Set the color. switch (_shape) { case OVAL: g2.fillOval(_start.x, _start.y, _end.x - _start.x, _end.y - _start.y); break; case RECTANGLE: g2.fillRect(_start.x, _start.y, _end.x - _start.x, _end.y - _start.y); break; case LINE: g2.drawLine(_start.x, _start.y, _end.x , _end.y); break; default: // Should never happen! g2.drawString("Huh?", 10, 20); break; } } //============================================================= mousePressed public void mousePressed(MouseEvent e) { _state = State.DRAGGING; // Assume we're starting a drag. _start = e.getPoint(); // Save start point, and also initially _end = _start; // as end point, which drag will change. } //============================================================= mouseDragged public void mouseDragged(MouseEvent e) { _state = State.DRAGGING; // We're dragging to create a shape. _end = e.getPoint(); // Set end point of drag. May change. this.repaint(); // After change, show new shape } //============================================================ mouseReleased public void mouseReleased(MouseEvent e) { //... If released at end of drag, write shape into the BufferedImage, // which saves it in the drawing. _end = e.getPoint(); // Set end point of drag. if (_state == State.DRAGGING) { _state = State.IDLE; //... Draw current shape in saved buffered image. drawCurrentShape(_bufImage.createGraphics()); this.repaint(); } } //================================================== ignored mouse listeners public void mouseMoved (MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited (MouseEvent e) {} public void mouseClicked(MouseEvent e) {} } |
The enum defining possible shapes
1 2 3 4 5 6 7 8 |
// File : gui-lowlevel/paintdemo/Shape.java // Purpose: Defines the shapes that can be drawn. // Author : Fred Swartz - October 12, 2006 - Placed in public domain. package paintdemo; /////////////////////////////////////////////////////////////////// Shape public enum Shape { RECTANGLE, OVAL, LINE } |
Defining methods associated with enum constants
Whenever you see a switch statement based on an enum value, you might consider whether you can define a method in the enum class for handling this. Such a use of enums goes beyond what is available in many languages, and is quite useful. Here are sections of the above code rewritten in this form.
Simplifying the PaintPanel drawCurrentShape method
Compare the simplicity of this to the above version with a switch.
//========================================================= drawCurrentShape private void drawCurrentShape(Graphics2D g2) { g2.setColor(_color); // Set the color. _shape.draw(g2, _start, _end); // Draw the shape. }
Moving the drawing to the enum class
The drawing code has to go somewhere. The separate behaviors of the different shapes is probably more appropriately centralized with each shape as below.
////////////////////////////////////////////////////////////////////////// Shape public enum Shape { RECTANGLE { void draw(Graphics2D g2, Point from, Point to) { g2.fillRect(from.x, from.y, to.x - from.x, to.y - from.y); } }, OVAL { void draw(Graphics2D g2, Point from, Point to) { g2.fillOval(from.x, from.y, to.x - from.x, to.y - from.y); } }, LINE { void draw(Graphics2D g2, Point from, Point to) { g2.drawLine(from.x, from.y, to.x, to.y); } }; //... Must define methods implemented by all constants as abstract. // Note semicolon after last enum constant above. abstract void draw(Graphics2D g2, Point from, Point to); }