JPanel Subclass for Drawing with the Mouse
Section 11.13 showed how to track mouse events in a JPanel. In this section, we use a JPanel as a dedicated drawing area in which the user can draw by dragging the mouse. In addition, this section demonstrates an event listener that extends an adapter class.
Method paintComponent
Lightweight Swing components that extend class JComponent (such as JPanel) contain method paintComponent, which is called when a lightweight Swing component is displayed. By overriding this method, you can specify how to draw shapes using Java's graphics capabilities. When customizing a JPanel for use as a dedicated drawing area, the subclass should override method paintComponent and call the superclass version of paintComponent as the first statement in the body of the overridden method to ensure that the component displays correctly. The reason for this is that subclasses of JComponent support transparency. To display a component correctly, the program must determine whether the component is transparent. The code that determines this is in superclass JComponent's paintComponent implementation. When a component is transparent, paintComponent will not clear its background when the program displays the component. When a component is opaque, paintComponent clears the component's background before the component is displayed. If the superclass version of paintComponent is not called, an opaque GUI component typically will not display correctly on the user interface. Also, if the superclass version is called after performing the customized drawing statements, the results typically will be erased. The transparency of a Swing lightweight component can be set with method setOpaque (a false argument indicates that the component is transparent).
Look-and-Feel Observation 11.13
Most Swing GUI components can be transparent or opaque. If a Swing GUI component is opaque, its background will be cleared when its paintComponent method is called. Only opaque components can display a customized background color. JPanel objects are opaque by default. |
Error-Prevention Tip 11.1
In a JComponent subclass's paintComponent method, the first statement should always be a call to the superclass's paintComponent method to ensure that an object of the subclass displays correctly. |
Common Programming Error 11.5
If an overridden paintComponent method does not call the superclass's version, the subclass component may not display properly. If an overridden paintComponent method calls the superclass's version after other drawing is performed, the drawing will be erased. |
Defining the Custom Drawing Area
The Painter application of Fig. 11.34 and Fig. 11.35 demonstrates a customized subclass of JPanel that is used to create a dedicated drawing area. The application uses the mouseDragged event handler to create a simple drawing application. The user can draw pictures by dragging the mouse on the JPanel. This example does not use method mouseMoved, so our event-listener class (the anonymous inner class at lines 22-34) extends MouseMotionAdapter. Since, this class already declares both mouseMoved and mouseDragged, we can simply override mouseDragged to provide the event handling this application requires.
Figure 11.34. Adapter classes used to implement event handlers.
(This item is displayed on page 561 in the print version)
1 // Fig. 11.34: PaintPanel.java 2 // Using class MouseMotionAdapter. 3 import java.awt.Point; 4 import java.awt.Graphics; 5 import java.awt.event.MouseEvent; 6 import java.awt.event.MouseMotionAdapter; 7 import javax.swing.JPanel; 8 9 public class PaintPanel extends JPanel 10 { 11 private int pointCount = 0 ; // count number of points 12 13 // array of 10000 java.awt.Point references 14 private Point points[] = new Point[ 10000 ]; 15 16 // set up GUI and register mouse event handler 17 public PaintPanel() 18 { 19 // handle frame mouse motion event 20 addMouseMotionListener( 21 22 new MouseMotionAdapter() // anonymous inner class 23 { 24 // store drag coordinates and repaint 25 public void mouseDragged( MouseEvent event ) 26 { 27 if ( pointCount < points.length ) 28 { 29 points[ pointCount ] = event.getPoint(); // find point 30 pointCount++; // increment number of points in array 31 repaint(); // repaint JFrame 32 } // end if 33 } // end method mouseDragged 34 } // end anonymous inner class 35 ); // end call to addMouseMotionListener 36 } // end PaintPanel constructor 37 38 // draw oval in a 4-by-4 bounding box at specified location on window 39 public void paintComponent( Graphics g ) 40 { 41 super.paintComponent( g ); // clears drawing area 42 43 // draw all points in array 44 for ( int i = 0 ; i < pointCount; i++ ) 45 g.fillOval( points[ i ].x, points[ i ].y, 4, 4 ); 46 } // end method paintComponent 47 } // end class PaintPanel |
Figure 11.35. Test class for PaintFrame.
(This item is displayed on page 563 in the print version)
1 // Fig. 11.35: Painter.java 2 // Testing PaintPanel. 3 import java.awt.BorderLayout; 4 import javax.swing.JFrame; 5 import javax.swing.JLabel; 6 7 public class Painter 8 { 9 public static void main( String args[] ) 10 { 11 // create JFrame 12 JFrame application = new JFrame( "A simple paint program" ); 13 14 PaintPanel paintPanel = new PaintPanel(); // create paint panel 15 application.add( paintPanel, BorderLayout.CENTER ); // in center 16 17 // create a label and place it in SOUTH of BorderLayout 18 application.add( new JLabel( "Drag the mouse to draw" ), 19 BorderLayout.SOUTH ); 20 21 application.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); 22 application.setSize( 400, 200 ); // set frame size 23 application.setVisible( true ); // display frame 24 } // end main 25 } // end class Painter
|
Class PaintPanel (Fig. 11.34) extends JPanel to create the dedicated drawing area. Lines 37 import the classes used in class PaintPanel. Class Point (package java.awt) represents an x-y coordinate. We use objects of this class to store the coordinates of each mouse drag event. Class Graphics is used to draw.
In this example, we use an array of 10000 Points (line 14) to store the location at which each mouse-drag event occurs. As you will see, method paintComponent uses these Points to draw. Instance variable pointCount (line 11) maintains the total number of Points captured from mouse drag events so far.
Lines 2035 register a MouseMotionListener to listen for the PaintPanel's mouse-motion events. Lines 2234 create an object of an anonymous inner class that extends the adapter class MouseMotionAdapter. Recall that MouseMotionAdapter implements MouseMotionListener, so the anonymous inner class object is a MouseMotionListener. The anonymous inner class inherits a default implementation of methods mouseMoved and mouseDragged, so it already satisfies the requirement that all methods of the interface must be implemented. However, the default methods do nothing when they are called. So, we override method mouseDragged at lines 2533 to capture the coordinates of a mouse dragged event and store them as a Point object. Line 27 ensures that we store the event's coordinates only if there are still empty elements in the array. If so, line 29 invokes the MouseEvent's getPoint method to obtain the Point where the event occurred and stores it in the array at index pointCount. Line 30 increments the pointCount, and line 31 calls method repaint (inherited indirectly from class Component) to indicate that the PaintPanel should be refreshed on the screen as soon as possible with a call to the PaintPanel's paintComponent method.
Method paintComponent (lines 3946), which receives a Graphics parameter, is called automatically any time the PaintPanel needs to be displayed on the screen (such as when the GUI is first displayed) or refreshed on the screen (such as when method repaint is called or when the GUI component was hidden by another window on the screen and subsequently becomes visible again).
Look-and-Feel Observation 11.14
Calling repaint for a Swing GUI component indicates that the component should be refreshed on the screen as soon as possible. The background of the GUI component is cleared only if the component is opaque. JComponent method setOpaque can be passed a boolean argument indicating whether the component is opaque (true) or transparent (false). |
Line 41 invokes the superclass version of paintComponent to clear the PaintPanel's background (JPanels are opaque by default). Lines 4445 draw an oval at the location specified by each Point in the array (up to the pointCount). Graphics method fillOval draws a solid oval. The method's four parameters represent a rectangular area (called the bounding box) in which the oval is displayed. The first two parameters are the upper-left x-coordinate and the upper-left y-coordinate of the rectangular area. The last two coordinates represent the rectangular area's width and height. Method fillOval draws the oval so it touches the middle of each side of the rectangular area. In line 45, the first two arguments are specified by using class Point's two public instance variablesx and y. The loop terminates either when a null reference is encountered in the array or when the end of the array is reached. You will learn more Graphics features in Chapter 12.
Look-and-Feel Observation 11.15
Drawing on any GUI component is performed with coordinates that are measured from the upper-left corner (0, 0) of that GUI component, not the upper-left corner of the screen. |
Using the Custom JPanel in an Application
Class Painter (Fig. 11.35) contains the main method that executes this application. Line 14 creates a PaintPanel object on which the user can drag the mouse to draw. Line 15 attaches the PaintPanel to the JFrame.