4-7 A Line Drawing App

4-7 A Line Drawing App

Let’s apply what we’ve learned about Swing components, drawing shapes, layout managers, and mouse event handling to create a simple mouse-controlled line drawing application.

Using a BorderLayout, we’ll include a status label in the NORTH region of the main JFrame showing the current mouse coordinates. In the CENTER region we’ll design a custom JPanel that will handle mouse events to enable the drawing of Line objects.

This time I’ll start with the test code:

To keep the DrawPanel code as clear as possible, we will use a (regular) inner class called MouseEventListener to handle mouse events.  This class will inherit from MouseAdapter such that we only need to override the mouse event methods that we need to.  Let’s take a look at the DrawPanel code:

You’ll note on line 12 that I am using an array of 1000 Line objects to store the lines as they are drawn — I’ll leave it as an exercise for you to use a dynamic data structure instead.  In 4-2 Q1 you designed a complete class hierarchy for Line, Rectangle, and Oval objects.  To test the code above, use your Line class.

On lines 15 and 16 the DrawPanel constructor receives a reference to the statusLabel created in main().  A copy of this reference is kept so that it can be updated by the event handling methods in the DrawPanel class.  There are four mouse events that we are interested in.

When the mouse is pressed we instantiate a new Line object and set its starting and ending coordinates to where the mouse button was pressed.  This currentLine is not considered complete until the user releases the mouse, so we will show it in Color.RED to indicate this to the user.  Since we do not have a reference to the Graphics object g, and therefore cannot directly call the paintComponent() method, the repaint(); statement  tells the JVM to call paintComponent() for us (more on this method below).

When the mouse is released, we use the mouse coordinates to update the (x2, y2) coordinate of the currentLine and switch it to Color.BLACK to indicate to the user that it is not currently being drawn.  As long as there is space in the lines array we add the currentLine to the array.  Again a call to repaint() will redraw all of the lines so that we see this latest update.

When the mouse is dragged (i.e., button down) it means that a currentLine has been started by mousePressed(), so we simply update the end coordinates of the currentLine, update the mouse location in the statusLabel, and call repaint() to reflect all of these changes.

When the mouse is moved (i.e., button up) we simply update the mouse location in the statusLabel.  No new lines have been created so there is no need to call repaint() to update our JPanel.

Now consider the paintComponent() method.  This method is called automatically by the JVM whenever the window is resized, but we can force it to be executed by calling the repaint() method.  The paintComponent() method simply calls the draw() method for each Line object in the array.  We have to be careful not to index past the point where Line objects are found or we will get a NullPointerException when calling draw().  Lastly, if a currentLine is in the process of being drawn then we draw the currentLine as well, on top of those from the lines array.

That’s all there is to it!  You can see that the bulk of the logic for this program happens in the mouse event handling methods.

You Try!

  1. Try changing the array size to 10 to see how the Line Drawing App behaves. Enhance the DrawPanel to use an ArrayList instead of an array.
  2. Change the code such that it draws Rectangle or Oval objects instead.