Java: Example - PaintDemo.java

This simple paint program leaves much to be desired, but it is a good example of event-driven programming, showing how buttons, mouse listeners, and paintComponent() interact thru instance variables.

The standard Graphics class methods are used with two additions. The BufferedImage object is used to accumulate the effect of all previous drawings. The paintComponent() method first draws the BufferedImage, then draws the current shape, which may be changing as the mouse is being dragged. When the mouse is released, the current shape is drawn into the BufferedImage. 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 paintComponent(). After downcasting, all Graphics2D operations become available.
  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 
149 
150 
151 
152 
153 
154 
155 
156 
157 
158 
159 
160 
161 
162 
163 
164 
165 
166 
167 
168 
169 
170 
171 
172 
173 
174 
175 
176 
177 
178 
179 
180 
181 
182 
183 
184 
185 
186 
187 
// PaintDemo.java - Simple painting program.
// Illustrates use of mouse and BufferedImage.
// Fred Swartz 2002-12-02
// Possible Enhancements:
//   * Clear drawing area
//   * Other shapes (line, outline shapes, text, ...)
//   * Color selection.
//   * An eraser.
//   * Create a real toobar.
//   * Save/Load 


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.image.BufferedImage;

////////////////////////////////////////////////////////////// PaintDemo
class PaintDemo {
    //============================================================= main
    public static void main(String[] args) {
        PaintWindow window = new PaintWindow();
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.setVisible(true);
    }//end main
}//endclass PaintDemo


//////////////////////////////////////////////////////////// PaintWindow
class PaintWindow extends JFrame {
    PaintPanel canvas = new PaintPanel();
    //====================================================== constructor
    public PaintWindow() {
        //--- create the buttons
        JButton circleButton = new JButton("Circle");
        circleButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                canvas.setShape(PaintPanel.CIRCLE);
            }});
        JButton rectangleButton = new JButton("Rectangle");
        rectangleButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                canvas.setShape(PaintPanel.RECTANGLE);
            }});
        
        //--- layout the buttons
        JPanel buttonPanel = new JPanel();
        buttonPanel.setLayout(new GridLayout(2, 1));
        buttonPanel.add(circleButton);
        buttonPanel.add(rectangleButton);
        
        //--- layout the window
        Container content = this.getContentPane();
        content.setLayout(new BorderLayout());
        content.add(buttonPanel, BorderLayout.WEST);
        content.add(canvas     , BorderLayout.CENTER);
        this.setTitle("Paint Demo");
        this.pack();
    }//end constructor
}//endclass PaintWindow


///////////////////////////////////////////////////////////// PaintPanel
class PaintPanel extends JPanel implements MouseListener, 
                                           MouseMotionListener {
    //--- Public constants used to specify shape being drawn.          
    public static final int NONE      = 0;
    public static final int LINE      = 1;
    public static final int RECTANGLE = 2;
    public static final int CIRCLE    = 3;
    
    //--- Variables to store the current figure info
    private int _shape = NONE;
    private int _currentStartX = 0;  // where mouse first pressed
    private int _currentStartY = 0;
    private int _currentEndX   = 0;  // where dragged to or released
    private int _currentEndY   = 0;
    
    //--- BufferedImage to store the underlying saved painting.
    //    Will be initialized first time paintComponent is called.
    private BufferedImage _bufImage = null;
    
    //--- Private constant for size of paint area.
    private static final int SIZE = 600; // size of paint area
    
    
    //====================================================== constructor
    public PaintPanel() {
        setPreferredSize(new Dimension(SIZE, SIZE));
        setBackground(Color.white);
        //--- Add the mouse listeners.
        this.addMouseListener(this); 
        this.addMouseMotionListener(this);
    }//endconstructor
    
    
    //========================================================= setShape
    public void setShape(int shape) {
        //--- Provided so users can set the shape.
        _shape = shape;
    }//end setShape
    
    
    //=================================================== paintComponent
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        
        Graphics2D g2 = (Graphics2D)g;  // downcast to Graphics2D
        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.white);
            gc.fillRect(0, 0, w, h); // fill in background
        }
        g2.drawImage(_bufImage, null, 0, 0);  // draw previous shapes
        
        drawCurrentShape(g2);
    }//end paintComponent
    
    
    //================================================= 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.
        switch (_shape) {
            case NONE  :
                     break;
                     
            case CIRCLE:
                     g2.fillOval(_currentStartX, _currentStartY,
                                 _currentEndX - _currentStartX, 
                                 _currentEndY - _currentStartY);
                     break;
                     
            case RECTANGLE:
                     g2.fillRect(_currentStartX, _currentStartY,
                                 _currentEndX - _currentStartX, 
                                 _currentEndY - _currentStartY);
                     break;
                     
            default:  // should never happen
                     g2.drawString("Huh?", 10, 20);
                     break;
        }
    }//end paintComponent

    //===================================================== mousePressed
    public void mousePressed(MouseEvent e) {
        _currentStartX = e.getX(); // save x coordinate of the click
        _currentStartY = e.getY(); // save y
        _currentEndX   = _currentStartX;   // set end to same pixel
        _currentEndY   = _currentStartY;
    }//end mousePressed

    //===================================================== mouseDragged
    public void mouseDragged(MouseEvent e) {
        _currentEndX = e.getX();   // save new x and y coordinates
        _currentEndY = e.getY();
        this.repaint();            // show new shape
    }//end mouseDragged
    
    //==================================================== mouseReleased
    public void mouseReleased(MouseEvent e) {
        // This will save the shape that has been dragged by
        // drawing it onto the bufferedImage where all shapes
        // are written.
        _currentEndX = e.getX(); // save ending coordinates
        _currentEndY = e.getY();
        
        //--- Draw the current shape onto the buffered image.
        Graphics2D grafarea = _bufImage.createGraphics();
        drawCurrentShape(grafarea);
        
        this.repaint();
    }//end mouseReleased
    
    //========================================== ignored mouse listeners
    public void mouseMoved   (MouseEvent e) {}
    public void mouseEntered (MouseEvent e) {}
    public void mouseExited  (MouseEvent e) {}
    public void mouseClicked (MouseEvent e) {}
}//endclass PaintPanel