简体   繁体   中英

How to use drawOval() method correctly?

Ok, so I am trying to make a paint project and I am successful so far. I have a "canvas" to draw on and I have a toolbar menu with the color choices. I have made a button and method with drawLine() which takes my current coordinates via the mouse motion listener and my end coordinates, basically drawing the line wherever and however I like on the canvas. Now I want to add a button that when clicked, will draw an oval/circle on my canvas. Here come the problems tho.

First problem - I can get it to draw the oval on my mouse coordinates, but I cannot drag in order to change its size before I release the mouse button and actually paint it (just like you would in Microsoft paint).

Second problem - When I have selected my "Line" button which invokes my draw line method, I can draw lines and its fine, but then when I click the "Oval" button, it draws the Oval but also puts a line when I click the mouse (I am assuming that I need to disable the mouse motion listener on the Line when I select the Oval.) The Opposite is also true, If I have selected the "Oval" button and drawn ovals before, then click the "Line" button, it will draw a line but also put an oval each time I click to start drawing a line.

This is how my simple program looks like: Image

Here is part of the code concerning the drawLine, drawOval and my coordinate gathering methods, since everything else is working as intended:

    // Image in which we're drawing.
    private Image image;
    // Graphics2D object which we used to draw on.
    private Graphics2D g2;
    // Mouse coordinates
    private int currentX, currentY, oldX, oldY;

    public DrawArea(){
        setDoubleBuffered(false);
        addMouseListener(new MouseAdapter(){

            public void mousePressed(MouseEvent e){
                //save coordinates x,y when mouse is pressed.
                oldX=e.getX();
                oldY=e.getY();
            }});

    }

protected void paintComponent(Graphics g){
    if (image==null){
        //image to draw null ==> we create
        image = createImage(getSize().width, getSize().height);
        g2 = (Graphics2D) image.getGraphics();
        //enable antialiasing.
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                RenderingHints.VALUE_ANTIALIAS_ON);
        //clear draw area
        clear();
    }

    g.drawImage(image, 0, 0, null);
}

public void clear(){
    g2.setPaint(Color.white);
    //draw white on entire draw are to clear it.
    g2.fillRect(0, 0, getSize().width, getSize().height);
    g2.setPaint(Color.black);
    repaint();
}

public void Line(){
    addMouseMotionListener(new MouseMotionAdapter(){
        public void mouseDragged(MouseEvent e){
            //coordinates x,y when dragging mouse.
            currentX=e.getX();
            currentY=e.getY();
            if (g2 != null){
                g2.drawLine(oldX, oldY, currentX, currentY);
                repaint();
                oldX = currentX;
                oldY = currentY;
            }   
        }
    }); 
}

public void Oval(){
    addMouseListener(new MouseAdapter(){
        public void mousePressed(MouseEvent e){
            currentX=e.getX();
            currentY=e.getY();
            if (g2 != null){
                g2.drawOval(oldX, oldY, currentX, currentY);
                repaint();
            }   
        }
    }); 
}

First problem - I can get it to draw the oval on my mouse coordinates, but I cannot drag in order to change its size before I release the mouse button and actually paint it (just like you would in Microsoft paint).

You'd could repaint the entire canvas every time you move the mouse but you probably don't want to do that. Instead you might want to use a second canvas to draw the temporary oval (or lines, boxes etc.) onto and repaint that whenever you drag the mouse. That's not that expensive since you'll only need one or a few of draw calls.

When releasing the mouse you then draw the shape you currently selected onto the more persistent canvases. One way to achieve this would be to use a BufferedImage as your persistent canvas and your panel (or whatever canvas you're using) as the temporary one. Then when dragging the mouse you paint the image to the panel first then add the shape. When releasing the mouse you draw the shape onto the image and then just draw the image to the panel and be done.

That way you'd already have an image object you could write to a file if needed.

Edit : looking at your code you seem to already have the image in place. So just change the graphics object your drawing temporary shapes to: as long as the mouse is being dragged, you could directly use the component you're drawing to.

Second problem - When I have selected my "Line" button which invokes my draw line method, I can draw lines and its fine, but then when I click the "Oval" button, it draws the Oval but also puts a line when I click the mouse (I am assuming that I need to disable the mouse motion listener on the Line when I select the Oval.)

Yes, you should probably only have one listener used for drawing (although the listener drawing itself seems to be questionable design, but I'll skip that for now). That listener records the mouse position when you pressed the button as well as the mouse position after dragging it or releasing the button again.

When releasing the button you'd check whether the position is inside the canvas or not and if not just forget the rest. Thus you won't get any shapes drawn when clicking a button.

Additionally, clicking a button should probably just change the shape you're about to draw (either by setting some variable or by replacing the object that handles the actual drawing). The mouse listener would then just use that information to draw the currently selected shape (if there is any) - ideally by informing the "drawer" about any relevant events.

Edit : example on using only one listener (simplified, ie without any checks, color etc.).

interface Renderer {
  void draw( Graphics2D g, Point start, Point end );
}

class OvalRenderer implements Renderer {
  public void draw( Graphics2D g, Point start, Point end ) {
    g.drawOval(start.getX(), start.getY(), end.getY(), end.getY() );
  }
}

class CanvasListener extends MouseAdapter {
  Point start;
  public void mousePressed(MouseEvent e){
    start = e.getPoint(); 
  }

  public void mouseDragged(MouseEvent e){  
    //currentRenderer is defined elsewhere and just made accessible to the listener
    currentRenderer.draw( component.getGraphics(), start, e.getPoint() );
  }

  public void mouseReleased(MouseEvent e){  
    currentRenderer.draw( image.getGraphics(), start, e.getPoint() );
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM