views:

3191

answers:

4

I have my program that can draw rectangles. I have two problems I can't solve. After I draw the rectangle it won't stay. The only code I have that clears the canvas in under paint, repaint is only called on mouse drag. Why when I mouse release or mouse move does my canvas clear. The second thing isn't as much a problem, but something I can't figure out, when either the height or width of my rectangle is negative the rectangle is filled in black.

package pracpapp2;


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

public class MouseTracker4July extends JFrame
   implements MouseListener, MouseMotionListener {


  private static final long serialVersionUID = 1L;
  private JLabel mousePosition;
  int x, y;
  int x1, x2, y1, y2;
  int w, h;
  private JLabel recStart;
  private JLabel recStop;
  private JLabel cords;
   // set up GUI and register mouse event handlers
   public MouseTracker4July()
   {
      super( "Rectangle Drawer" );

      mousePosition = new JLabel();
      mousePosition.setHorizontalAlignment(SwingConstants.CENTER);
      getContentPane().add( mousePosition, BorderLayout.CENTER );


      JLabel text1 = new JLabel();
      text1.setText( "At the center the mouse pointer's coordinates will be displayed." );
      getContentPane().add( text1, BorderLayout.SOUTH );

      recStart = new JLabel();
      getContentPane().add(recStart, BorderLayout.WEST);

      recStop = new JLabel();
      getContentPane().add(recStop, BorderLayout.EAST);

      cords = new JLabel();
      getContentPane().add(cords, BorderLayout.NORTH);


      addMouseListener( this );        // listens for own mouse and
      addMouseMotionListener( this );  // mouse-motion events

      setSize( 800, 600 );
      setVisible( true );
   }

   // MouseListener event handlers
   // handle event when mouse released immediately after press
   public void mouseClicked( MouseEvent event )
   {
      mousePosition.setText( "Clicked at [" + event.getX() +
         ", " + event.getY() + "]" );
   }

   // handle event when mouse pressed
   public void mousePressed( MouseEvent event )
   {

      mousePosition.setText( "Pressed at [" +(x1 = event.getX()) +
         ", " + (y1 = event.getY()) + "]" );

      recStart.setText( "Start:  [" + x1 +
         ", " + y1 + "]" );
   }

   // handle event when mouse released after dragging
   public void mouseReleased( MouseEvent event )
   {
     mousePosition.setText( "Released at [" +(x2 = event.getX()) +
         ", " + (y2 = event.getY()) + "]" );

     recStop.setText( "End:  [" + x2 +
         ", " + y2 + "]" );

   }

   // handle event when mouse enters area
   public void mouseEntered( MouseEvent event )
   {
      mousePosition.setText( "Mouse entered at [" + event.getX() +
         ", " + event.getY() + "]" );
   }

   // handle event when mouse exits area
   public void mouseExited( MouseEvent event )
   {
      mousePosition.setText( "Mouse outside window" );
   }

   // MouseMotionListener event handlers
   // handle event when user drags mouse with button pressed
   public void mouseDragged( MouseEvent event )
   {
      mousePosition.setText( "Dragged at [" + (x = event.getX()) + 
         ", " + (y = event.getY()) + "]" );
      // call repaint which calls paint
      repaint();

   }

   // handle event when user moves mouse
   public void mouseMoved( MouseEvent event )
   {
      mousePosition.setText( "Moved at [" + event.getX() +
         ", " + event.getY() + "]" );
   }

   public void paint(Graphics g)
   {
      super.paint(g); // clear the frame surface
      g.drawString("Start Rec Here", x1, y1);
      g.drawString("End Rec Here", x, y);

      w = x1 - x;
      h = y1 - y;
      w = w * -1;
      h = h * -1;

      g.drawRect(x1, y1, w, h);

      cords.setText( "w = " + w + ", h = " + h);
   }

   public static void main( String args[] )
   { 
      MouseTracker4July application = new MouseTracker4July();
      application.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
   }

} // end class MouseTracker
+2  A: 

You need to store your drawn items in some data structure and ensure that each item in the structure is painted to the canvas on repaint.

Also, you need to add repaint to each of your mouse events.

Like this: (this assumes you want to keep ALL rect's) - you can go with a single rect by eliminating the arraylist and replacing with a single rect instance.

import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingConstants;

public class MouseTracker4July extends JFrame implements MouseListener, MouseMotionListener {

    private static final long serialVersionUID = 1L;
    private final JLabel mousePosition;
    int x1, x2, y1, y2;
    int w, h;
    private final JLabel recStart;
    private final JLabel recStop;
    private final JLabel cords; // set up GUI and register mouse event handlers
    private final ArrayList< Rectangle > rectangles = new ArrayList< Rectangle >();
    private boolean isNewRect = true;

    public MouseTracker4July() {
     super( "Rectangle Drawer" );

     this.mousePosition = new JLabel();
     this.mousePosition.setHorizontalAlignment( SwingConstants.CENTER );
     getContentPane().add( this.mousePosition, BorderLayout.CENTER );

     JLabel text1 = new JLabel();
     text1.setText( "At the center the mouse pointer's coordinates will be displayed." );
     getContentPane().add( text1, BorderLayout.SOUTH );

     this.recStart = new JLabel();
     getContentPane().add( this.recStart, BorderLayout.WEST );

     this.recStop = new JLabel();
     getContentPane().add( this.recStop, BorderLayout.EAST );

     this.cords = new JLabel();
     getContentPane().add( this.cords, BorderLayout.NORTH );

     addMouseListener( this ); // listens for own mouse and
     addMouseMotionListener( this ); // mouse-motion events

     setSize( 800, 600 );
     setVisible( true );

    }

// MouseListener event handlers // handle event when mouse released immediately after press 
    public void mouseClicked( final MouseEvent event ) {
     this.mousePosition.setText( "Clicked at [" + event.getX() + ", " + event.getY() + "]" );

     repaint();
    }

// handle event when mouse pressed 
    public void mousePressed( final MouseEvent event ) {

     this.mousePosition.setText( "Pressed at [" + ( this.x1 = event.getX() ) + ", " + ( this.y1 = event.getY() ) + "]" );

     this.recStart.setText( "Start:  [" + this.x1 + ", " + this.y1 + "]" );

     repaint();
    }

// handle event when mouse released after dragging 
    public void mouseReleased( final MouseEvent event ) {
     this.mousePosition.setText( "Released at [" + ( this.x2 = event.getX() ) + ", " + ( this.y2 = event.getY() ) + "]" );

     this.recStop.setText( "End:  [" + this.x2 + ", " + this.y2 + "]" );

     Rectangle rectangle = getRectangleFromPoints();

     this.rectangles.add( rectangle );

     this.w = this.h = this.x1 = this.y1 = this.x2 = this.y2 = 0;
     this.isNewRect = true;

     repaint();
    }

    private Rectangle getRectangleFromPoints() {
     int width = this.x1 - this.x2;
     int height = this.y1 - this.y2;
     Rectangle rectangle = new Rectangle( width < 0 ? this.x1
      : this.x2, height < 0 ? this.y1
      : this.y2, Math.abs( width ), Math.abs( height ) );

     return rectangle;
    }

// handle event when mouse enters area 
    public void mouseEntered( final MouseEvent event ) {
     this.mousePosition.setText( "Mouse entered at [" + event.getX() + ", " + event.getY() + "]" );
     repaint();
    }

// handle event when mouse exits area 
    public void mouseExited( final MouseEvent event ) {
     this.mousePosition.setText( "Mouse outside window" );
     repaint();
    }

// MouseMotionListener event handlers // handle event when user drags mouse with button pressed 
    public void mouseDragged( final MouseEvent event ) {
     this.mousePosition.setText( "Dragged at [" + ( this.x2 = event.getX() ) + ", " + ( this.y2 = event.getY() ) + "]" ); // call repaint which calls paint repaint();

     this.isNewRect = false;

     repaint();
    }

// handle event when user moves mouse 
    public void mouseMoved( final MouseEvent event ) {
     this.mousePosition.setText( "Moved at [" + event.getX() + ", " + event.getY() + "]" );
     repaint();
    }

    @Override
    public void paint( final Graphics g ) {
     super.paint( g ); // clear the frame surface 
     g.drawString( "Start Rec Here", this.x1, this.y1 );
     g.drawString( "End Rec Here", this.x2, this.y2 );

     Rectangle newRectangle = getRectangleFromPoints();
     if ( !this.isNewRect ) {
      g.drawRect( newRectangle.x, newRectangle.y, newRectangle.width, newRectangle.height );
     }

     for( Rectangle rectangle : this.rectangles ) {
      g.drawRect( rectangle.x, rectangle.y, rectangle.width, rectangle.height );
     }

     this.cords.setText( "w = " + this.w + ", h = " + this.h );

    }

    public static void main( final String args[] ) {
     MouseTracker4July application = new MouseTracker4July();
     application.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    }

} // end class MouseTracker
javamonkey79
This is my first time programming graphically. How could I store the entire rectangle. I already have the rectangle parts (x, y, w, h) stored as int, then the rectangle is being drawn, but it's my integers are constantly updating, I'm not sure how to store them to something static that can be held on the canvas.
Tyler
Custom painting should be done on a JPanel or JComponent, not on the actual JFrame and the paintComponent() method should be overriden, not the paint() method.
camickr
@camickr - I agree, but for sake of understanding the problem I don't know that it will help Tyler in this insance (in the long run, yes).
javamonkey79
@Tyler: Look at the example I reposted. It has an arraylist of rect's to draw - this will allow the rect's to be stored. If you only want 1 rect then get rid of the list and keep a single rect. OR - you can keep just the x,y,w,h stuff - depends on what you want to do.
javamonkey79
Thanks, I checked out your example, I know it works, I'm just trying to figure out how everything works (new java programmer). Anyways, I appreciate the help, but it's late and code is starting to melt my brain. I'll hit it back up tomorrow. Thanks, again.
Tyler
A: 

Read up on these two Custom Painting Approaches. One approach is described above and the second approach shows how to use a BufferedImage. The example used for both approaches allows you to add multiple Rectangles to the frame.

camickr
+2  A: 

Ok, after re-reading your question it seems you could care less to have multiple rectangles :)

Here is a solution with only one at a time (which is close to what you had to begin with):

import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingConstants;

public class MouseTracker4July extends JFrame implements MouseListener, MouseMotionListener {

    private static final long serialVersionUID = 1L;
    private final JLabel mousePosition;
    int x1, x2, y1, y2;
    int x, y, w, h;
    private final JLabel recStart;
    private final JLabel recStop;
    private final JLabel cords; // set up GUI and register mouse event handlers
    boolean isNewRect = true;

    public MouseTracker4July() {
     super( "Rectangle Drawer" );

     this.mousePosition = new JLabel();
     this.mousePosition.setHorizontalAlignment( SwingConstants.CENTER );
     getContentPane().add( this.mousePosition, BorderLayout.CENTER );

     JLabel text1 = new JLabel();
     text1.setText( "At the center the mouse pointer's coordinates will be displayed." );
     getContentPane().add( text1, BorderLayout.SOUTH );

     this.recStart = new JLabel();
     getContentPane().add( this.recStart, BorderLayout.WEST );

     this.recStop = new JLabel();
     getContentPane().add( this.recStop, BorderLayout.EAST );

     this.cords = new JLabel();
     getContentPane().add( this.cords, BorderLayout.NORTH );

     addMouseListener( this ); // listens for own mouse and
     addMouseMotionListener( this ); // mouse-motion events

     setSize( 800, 600 );
     setVisible( true );

    }

// MouseListener event handlers // handle event when mouse released immediately after press 
    public void mouseClicked( final MouseEvent event ) {
     this.mousePosition.setText( "Clicked at [" + event.getX() + ", " + event.getY() + "]" );

     repaint();
    }

// handle event when mouse pressed 
    public void mousePressed( final MouseEvent event ) {

     this.mousePosition.setText( "Pressed at [" + ( this.x1 = event.getX() ) + ", " + ( this.y1 = event.getY() ) + "]" );

     this.recStart.setText( "Start:  [" + this.x1 + ", " + this.y1 + "]" );

     this.isNewRect = true;

     repaint();
    }

// handle event when mouse released after dragging 
    public void mouseReleased( final MouseEvent event ) {
     this.mousePosition.setText( "Released at [" + ( this.x2 = event.getX() ) + ", " + ( this.y2 = event.getY() ) + "]" );

     this.recStop.setText( "End:  [" + this.x2 + ", " + this.y2 + "]" );

     repaint();
    }

// handle event when mouse enters area 
    public void mouseEntered( final MouseEvent event ) {
     this.mousePosition.setText( "Mouse entered at [" + event.getX() + ", " + event.getY() + "]" );
     repaint();
    }

// handle event when mouse exits area 
    public void mouseExited( final MouseEvent event ) {
     this.mousePosition.setText( "Mouse outside window" );
     repaint();
    }

// MouseMotionListener event handlers // handle event when user drags mouse with button pressed 
    public void mouseDragged( final MouseEvent event ) {
     this.mousePosition.setText( "Dragged at [" + ( this.x2 = event.getX() ) + ", " + ( this.y2 = event.getY() ) + "]" ); // call repaint which calls paint repaint();

     this.isNewRect = false;

     repaint();
    }

// handle event when user moves mouse 
    public void mouseMoved( final MouseEvent event ) {
     this.mousePosition.setText( "Moved at [" + event.getX() + ", " + event.getY() + "]" );
     repaint();
    }

    @Override
    public void paint( final Graphics g ) {
     super.paint( g ); // clear the frame surface 
     g.drawString( "Start Rec Here", this.x1, this.y1 );
     g.drawString( "End Rec Here", this.x2, this.y2 );

     int width = this.x1 - this.x2;
     int height = this.y1 - this.y2;

     this.w = Math.abs( width );
     this.h = Math.abs( height );
     this.x = width < 0 ? this.x1
      : this.x2;
     this.y = height < 0 ? this.y1
      : this.y2;

     if ( !this.isNewRect ) {
      g.drawRect( this.x, this.y, this.w, this.h );
     }

     this.cords.setText( "w = " + this.w + ", h = " + this.h );

    }

    public static void main( final String args[] ) {
     MouseTracker4July application = new MouseTracker4July();
     application.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    }

} // end class MouseTracker
javamonkey79
That's exactly what I needed, I knew I was close, I just couldn't figure out how to finish the thing. The math.abs is cool because it keeps my width and height positive . Thanks a ton. One more question, is all the "this." good form or personal preference? I'm just curious because since I'm only using one JFrame isn't it redundant? Thanks again.
Tyler
There is a lot of flicker every time I drag the mouse around, which is caused by overriding the paint() method of the frame as I suggested earlier.
camickr
Personally, I would say that the 'this' usage is a bit of preference - there are others who would militantly disagree. Glad to help.
javamonkey79
A: 

If you don't care some display information, just delete every "mousePosition.setText(...)" in the mouse listeners, they will result the unnecessary repaint() callings.

Then, add two fields: "int rx, ry;", add/modify several methods as below:

        public void mouseDragged(MouseEvent event) {
//   mousePosition.setText("Dragged at [" + (x = event.getX()) + ", "
//     + (y = event.getY()) + "]");
     // call repaint which calls paint
     x = event.getX();
     y = event.getY();

     compRectPos();
     repaint();
    }

    private void compRectPos()
    {
     rx = x1;
     ry = y1;

     w = x - x1;
     h = y - y1;

     if ( w < 0)
      rx += w;
     if (h < 0)
      ry += h;

     w = Math.abs(w);
     h = Math.abs(h);

    }

    public void paint(Graphics g) {
     super.paint(g); // clear the frame surface
     g.drawString("Start Rec Here", x1, y1);
     g.drawString("End Rec Here", x, y);

     g.drawRect(rx, ry, w, h);

     cords.setText("w = " + w + ", h = " + h);
    }

The only problem I found is, that the rectangle doesn't appear when first drawn.

pianyao