0

I have a combobox in which I can choose to draw either a rectangle, a circle or by freehand. If I choose to draw a circle it draws it perfectly. If I then switch to draw a rectangle it draws a circle inside the rectangle. The same happens if I first choose to draw a rectangle and then a circle. (See Picture below)

My questions are:

  1. How can I switch between drawing a circle and a rectangle without the circle appearing inside the rectangle?

  2. How can I get the rectangle/circle to show while I'm dragging the mouse. What I mean is how can the lines show until I release the mouse click?

  3. Why doesn't it work drawing by free hand?

enter image description here

This is my testclass:

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

public class Lab6 extends JFrame implements ActionListener {
    int startX, startY, endX, endY, w, h;
    ArrayList<Shape> shapeList =  new ArrayList<Shape>();
    Container cp = getContentPane();

    private JPanel topPanel;
    private JComboBox comboBox;    
    private final String[] boxOptions = new String[] {"Rektangel", "Cirkel", "Frihand"};   


    public Lab6(String title) {
        super(title);
        this.setLayout(new BorderLayout());
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        this.setLocationRelativeTo(null);        
        this.setSize(840, 500);    
        this.initComponents();
        this.setVisible(true);
    }


    private void initComponents() {
        topPanel = new JPanel(new GridLayout(1,2));     
        topPanel.setPreferredSize(new Dimension(0,40));

        comboBox = new JComboBox(boxOptions);
        comboBox.setSelectedIndex(0);          
        comboBox.addActionListener(this);                          

        topPanel.add(comboBox);
        this.add(topPanel, BorderLayout.PAGE_START);          
    }

    @Override
    public void paint(Graphics g) {
        for (Shape s : shapeList) {
            s.draw(g);
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource().equals(comboBox)) {     
            JComboBox cb = (JComboBox)e.getSource();
            if (cb.getSelectedItem().equals("Rektangel")) {                
                cp.addMouseListener(new MouseAdapter() {            
                    @Override
                    public void mousePressed(MouseEvent e) {                   
                        startX = e.getX();     
                        startY = e.getY(); 
                    }
                    @Override
                    public void mouseReleased(MouseEvent e) {
                        endX = e.getX();
                        endY = e.getY();

                        int width = startX - endX;
                        int height = startY - endY;
                        w = Math.abs(width);
                        h = Math.abs(height);

                        Rectangle r =  new Rectangle(startX, startY, w, h);

                        shapeList.add(r);
                        repaint();
                    }
                });                  
            }
            else if (cb.getSelectedItem().equals("Cirkel")) {

                cp.addMouseListener(new MouseAdapter() {            
                    @Override
                    public void mousePressed(MouseEvent e) {                   
                        startX = e.getX();     
                        startY = e.getY(); 
                    }
                    @Override
                    public void mouseReleased(MouseEvent e) {
                        endX = e.getX();
                        endY = e.getY();

                        int width = startX - endX;
                        int height = startY - endY;
                        w = Math.abs(width);
                        h = Math.abs(height);

                        Circle c =  new Circle(startX, startY, w, h);

                        shapeList.add(c);
                        repaint();
                    }
                });   
            }
            else if (cb.getSelectedItem().equals("Frihand")) {   //I need help with this part

               cp.addMouseListener(new MouseAdapter() {            
                @Override
                public void mousePressed(MouseEvent e) {            
                    startX = e.getX();
                    startY = e.getY();
                }
                @Override
                public void mouseDragged(MouseEvent e) {

                    FreeHand fh =  new FreeHand(startX, startY, e.getX(), e.getY());

                    shapeList.add(fh);
                    repaint();
                }
            });  

            }
        }
    }

    public static void main(String args[]) {
        new Lab6("Drawing Program");
    }
}

In class Rectangle (class Circle looks the same):

import java.awt.*;

public class Rectangle extends Shape {

    public Rectangle(int x, int y, int width, int height) {
        super(x, y, width, height);
    }

    public Rectangle() {
        super();
    }

    @Override
        public void draw(Graphics g) {  
            Graphics2D g2 = (Graphics2D) g;             
            g2.setColor(Color.RED);                     
            g2.setStroke(new BasicStroke(4));           
            g.drawRect(getX(), getY(), getWidth(), getHeight());       
    }        
}

In class FreeHand (I need help with this part):

import java.awt.*;

public class FreeHand extends Shape {

    public FreeHand(int x, int y, int width, int height) {
        super(x, y, width, height);
    }

    public FreeHand() {
        super();
    }        

    @Override
    public void draw(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;             
        g2.setColor(Color.BLUE);                    
        g2.setStroke(new BasicStroke(4));           
        g2.drawLine(getX(), getY(), getWidth(), getHeight());  
    }
}

In class shape:

import java.awt.Graphics;
import javax.swing.JPanel;

public abstract class Shape extends JPanel {

    private int startX, startY, width, height;    

    public Shape() {
        this(0, 0, 1, 1);
    }

    public Shape(int startX, int startY, int width, int height) {
        this.startX = startX;
        this.startY = startY;
        this.width = width;
        this.height = height;
    }

    public abstract void draw(Graphics g);

    @Override
    public int getX() {
        return startX;
    }

    @Override
    public int getY() {
        return startY;
    }

    @Override
    public int getWidth() {
        return width;
    }

    @Override
    public int getHeight() {
        return height;
    }
}
Mike G
  • 4,232
  • 9
  • 40
  • 66
user2939293
  • 793
  • 5
  • 16
  • 41

1 Answers1

3

There are a multitude of things going on...

  1. Overriding paint of JFrame
  2. Not calling super.paint before performing custom painting.
  3. Adding a new MosueListener EVERY time you change the shape

Instead, create a custom component, extending from something like JPanel and override it's paintComponent method. Use this component has your basic drawing surface (your controls should contained in another component).

Make sure you call super.paintComponent before performing any custom painting so you don't break the paint chain

See Performing Custom Painting and Painting in AWT and Swing for more details

Create a SINGLE MouseListener and register it to the panel. When the use selects a different shape, change a state variable within the panel (via a setter) which tells the MouseListener what it should do when the user starts drawing.

Updated...

Create a custom class that extends from JPanel...

public static class ShapePane extends JPanel {

}

Override the classes paintComponent method...

public static class ShapePane extends JPanel {

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        // Custom Painting here...
    }

}

Provide some sizing hints for the layout manager...

public static class ShapePane extends JPanel {

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        // Custom Painting here...
    }

    public Dimension getPreferredSize() {
        return new Dimension(200, 200);
    }

}

Provide a means by which the type of shape can be changed...so you know what to paint...

public static class ShapePane extends JPanel {

    public enum ShapeType {

        CIRCLE,
        RECTANGLE
    }

    private ShapeType currentShapeType;

    public void setCurrentShapeType(ShapeType currentShapeType) {
        this.currentShapeType = currentShapeType;
    }

    public ShapeType getCurrentShapeType() {
        return currentShapeType;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        // Custom Painting here...
    }

}

Add a SINGLE MouseListener to the custom class to create the required type of shapes...

public static class ShapePane extends JPanel {

    public enum ShapeType {

        CIRCLE,
        RECTANGLE
    }

    private ShapeType currentShapeType;

    public ShapePane() {
        addMouseListener(new MouseAdapter() {

            private Point clickPoint;

            @Override
            public void mousePressed(MouseEvent e) {
                clickPoint = e.getPoint();
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                Point releasePoint = e.getPoint();
                int x = Math.min(releasePoint.x, clickPoint.x);
                int y = Math.min(releasePoint.y, clickPoint.y);
                int width = Math.abs(clickPoint.x - releasePoint.x);
                int height = Math.abs(clickPoint.y - releasePoint.y);
                switch (getCurrentShapeType()) {
                    case CIRCLE:
                        // Make a circle
                        break;
                    case RECTANGLE:
                        // Make a rectangle...
                        break;
                }
                repaint();
            }

        });
    }

    public void setCurrentShapeType(ShapeType currentShapeType) {
        this.currentShapeType = currentShapeType;
    }

    public ShapeType getCurrentShapeType() {
        return currentShapeType;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        // Custom Painting here...
    }

}

Fill in the blanks...

Create another JPanel (you can simply create an instance this time), add your controls to it

Create an instance of a JFrame, add the custom class to it and the controls panel (make sure they are laid out correctly so they don't override each other - see Laying Out Components Within a Container for more details)

Use appropriate listeners to the controls to determine the type of shape the user wants to draw and set the currentShapeType property accordingly...

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Thank you for your answer, but unfortunately I don't understand what you mean. 1. Do you mean I should NOT override paint? If I remove @Override nothing Changes, the problem remains. 2. Where exactly in the code should I remove the call to super.paint? 3. Do you mean I should not add a new MouseListerner every time? How can I register the mouselistener to the panel? How till the code look like? Also, i'm not sure I understand what you mean by creating a custom Component, extending JPanel and override paintcomponent? Once again, thanks for your time. I appreciate it. – user2939293 Oct 30 '14 at 21:03
  • Start by reading the linked tutorials. Second, every time you select a shape type from the combobox, you are creating a new instance of a `MouseListener` add attaching it to your frame. This means if I choose circle, rectangle, circle, there are now THREE mouse listeners, each which will be notified when a mouse event occurs... – MadProgrammer Oct 30 '14 at 21:10
  • Thank you for explaining about the mouselistener. It cleared things out. How would the code look like to create one single mouselistener, and where do I put it? I will read the link, thanks again. – user2939293 Oct 30 '14 at 21:19
  • I'd add the listener in the constructor of the custom panel... – MadProgrammer Oct 30 '14 at 21:35
  • Ok, but how will the line of code for that look like in the constructor? – user2939293 Oct 30 '14 at 21:37
  • 1+ to Mad's answer. @user2939293: why not first try it out and see what you can come up, and see if it works? – Hovercraft Full Of Eels Oct 30 '14 at 21:45
  • It would look almost the same as it does when you create it within your listeners... – MadProgrammer Oct 30 '14 at 21:48