I have a JFrame containing 3 JPanels; Options, menu, canvas. In options there are a number of JButtons representing shapes. The aim is to click on the JButton of a shape e.g. rectangle, then click anywhere on the canvas and the shape will be drawn there.
For some reason, the shape does not always get drawn, it is only drawn when I click somewhere in the top left area of the canvas. Also the shape seems to randomly change size depending on where I click.
Here are some of my code snippets, it's probably a small error but I just can't seem to find it.
Shape:
public class Shape extends JPanel {
protected int xLocation;
protected int yLocation;
protected int numberOfSides;
protected String areaInfo;
protected String perimeterInfo;
public int getXLocation() {
return xLocation;
}
public void setXLocation(int xLocation) {
this.xLocation = xLocation;
}
public int getYLocation() {
return yLocation;
}
public void setYLocation(int yLocation) {
this.yLocation = yLocation;
}
public int getNumberOfSides() {
return numberOfSides;
}
public Shape(int xLocation, int yLocation, int numberOfSides) {
this.xLocation = xLocation;
this.yLocation = yLocation;
this.numberOfSides = numberOfSides;
}
}
Rectangle:
import java.awt.Color;
import java.awt.Graphics;
public class Rectangle extends Shape {
private int width;
private int height;
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public Rectangle(int xLocation, int yLocation, int width, int height ) {
super(xLocation, yLocation, 4);
this.width = width;
this.height = height;
this.areaInfo = "Multiply width * height";
this.perimeterInfo = "Add the lengths of each side";
}
public void paint(Graphics g){
g.setColor(Color.BLACK);
g.fillRect(xLocation, yLocation, width, height);
}
}
Canvas:
public class DrawingCanvas extends JPanel implements Serializable{
private ArrayList<Shape> shapeList;
OptionsPanel options;
public void addShape(Shape shape){
shapeList.add(shape);
this.add(shape);
this.repaint();
}
public DrawingCanvas(){
shapeList = new ArrayList<Shape>();
}
}
Frame:
public class DrawingFrame extends JFrame implements MouseListener, MouseMotionListener {
private OptionsPanel options;
private DrawingCanvas canvas;
private MenuBar menu;
Shape s; //shape to be manipulated
public DrawingFrame(){
options = new OptionsPanel();
canvas = new DrawingCanvas();
menu = new MenuBar();
//options.setBounds(0, 0, 100, 500);
options.setBackground(Color.GREEN);
canvas.setBackground(Color.yellow);
menu.setSize(1000,200);
menu.setBackground(Color.magenta);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(1000,500);
this.setTitle("Drawing Application");
this.setLayout(new BorderLayout());
this.getContentPane().add(options, BorderLayout.WEST);
this.getContentPane().add(canvas, BorderLayout.CENTER);
this.getContentPane().add(menu, BorderLayout.PAGE_START);
this.setVisible(true);
options.createRectangleButton.addMouseListener(this);
options.createSquareButton.addMouseListener(this);
options.createCircleButton.addMouseListener(this);
options.createTriangleButton.addMouseListener(this);
options.clearButton.addMouseListener(this);
canvas.addMouseListener(this);
canvas.addMouseMotionListener(this);
}
#Override
public void mouseClicked(MouseEvent e) {
boolean createShape = true;
if(e.getSource().equals(options.createRectangleButton)){
createShape = true;
s = new Rectangle(50,50,400,200);
s.addMouseListener(this);
s.addMouseMotionListener(this);
}
if (e.getSource().equals(canvas) && createShape == true){
s.setXLocation(e.getX());
s.setYLocation(e.getY());
createShape = false;
canvas.addShape(s);
}
Absent a complete example, it's hard to say. I'd expect your DrawingCanvas to override paintComponent() in order to render the accumulated Shape instances in shapeList. You might compare your approach to that shown in GaphPanel, cited here.
The code you provided is not complete, but anyway the problem is in your mouseClicked method, if you change your second if to something like the following for example:
if (e.getSource().equals(canvas) && createShape == true){
int x = e.getX();
int y = e.getY();
s = new Rectangle(x,y,x+50,y+50);
canvas.addShape(s);
}
then a rectangle of width & height 50 will be painted whenever you click on the canvas, depending on your x, y location (you could change the fixed width/height by using a variable based on user input). Also, I'm not sure what you're trying to do in your first if section where you're adding a MouseListener to a newly created shape that is not added to the canvas, I guess there's something else you want to do...
I had to overwrite the canvas class' paint method; call super.paint in the canvas class and repaint each shape individually
public void paint(Graphics g){
super.paint(g);
for(int i=0;i<shapeList.size();i++){
((Shape)shapeList.get(i)).paint(g);
}
}
Related
I am new to Java programming. I developing a java app, which draws shapes (circles, lines, triangles, etc) on a windows frame. I define an abstract class Shapes.java to contain the framework for shapes:
public abstract class Shapes {
public abstract void draw(Graphics g);
}
Then, I define some classes such as Circle, Line, Triangle, and Rectangle which extend from the Shapes.java class.
public class Circle extends Shapes{
private int x;
private int y;
private int radius;
public Circle(int x, int y, int radius) {
this.x = x;
this.y = y;
this.radius = radius;
}
#Override
public void draw(Graphics g) {
g.drawOval(x-radius,y-radius,radius * 2, radius *2);
}}
In my Picture.java class, I settle a JFrame and add shapes on it:
public class Picture extends JFrame {
private static final long serialVersionUID = 1L;
private int width;
private int height;
private boolean isClear = false;
private ArrayList<Shapes> listShape = new ArrayList<Shapes>();
private class ShapesPanel extends JPanel{
private static final long serialVersionUID = 1L;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if(isClear)
return;
else
for (Shapes s : listShape)
s.draw(g);
}
public void add(Shapes s){
listShape.add(s);
}
public Picture(int width, int height, String title) throws HeadlessException {
ShapesPanel mypanel = new ShapesPanel();
add(mypanel);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.width = width;
this.height = height;
this.setTitle(title);
}
public void draw(){
setLocationRelativeTo(null);
setSize(width, height);
setVisible(true);
repaint();
}
void clear(){//clear the componets in the JPanel
this.setIsClear(true);
this.validate();
this.repaint();
}
private void setIsClear(boolean b) {
// TODO Auto-generated method stub
this.isClear = b;
}
}
But when I invoke the clear() method in the main class, the program cannot repaint the new shapes again. How can I fix the bugs? Thanks.
public class MyPic {
public static void main(String[] args){
Picture pic = new Picture(420, 300, "shape demo");
Circle c1 = new Circle(320,80,80);
Rectangle r1 = new Rectangle(100,100,100,100);
Triangle t1 = new Triangle(100,100,200,100,150,50);
Line l1 = new Line(0,205,400,50);
pic.add(c1);
pic.add(r1);
pic.add(t1);
pic.add(l1);
pic.clear();
pic.draw();
pic.add(l1);//add l1 again
}
}
Okay, so by calling clear() you set a variable isClear to true. And then in your paintComponent you say:
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if(isClear)
return;
which means 'if isClear is true, don't paint anything' (which it is, you just set it to true with clear()). So, no wonder.
Anyway, I think in the clear method, you might want to do listShape.clear() instead of setting that boolean.
I'm trying to make the game snake and have run into an issue. What I have is a class called Segment which is used to create the objects that hold the x- and y-positions and also the direction of the snake. The class extends JPanel and overrides the method paintComponent(). I then add an object of type Segment to a JFrame in a different class. The methods I have for moving/changing directions of the snake (actually just a square at the moment) work perfectly but my problem is this:
When the square gets to about half of the width of the frame or half of the height of the frame it stops being drawn. I have made the background of the JPanel light grey so I know that the square hasn't reached the end of the JPanel when it stops. Below is my simple paintComponent() method and the section in the class that extends JFrame where I add the object.
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.setBackground(Color.LIGHT_GRAY);
g.setColor(Color.black);
g.fillRect(xpos, ypos, width, height);
}
public Snake() {
setLayout(new BorderLayout());
addKeyListener(l);
segment = new Segment(100, 100, Segment.Dir.RIGHT);
segment.setPreferredSize(new Dimension(500,500));
add(segment, BorderLayout.CENTER);
timer.start();
setVisible(true);
pack();
setTitle("Snake");
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
}
I also know that the objects position doesn't stop being updated so it's just the drawing of the square in the entire panel/frame that's the issue. Appreciate any help!
Here's an MCVE:
import java.awt.*;
import javax.swing.*;
public class Segment extends JPanel {
private int width = 10;
private int height = 10;
private int xpos, ypos;
private Dir dir;
public enum Dir {
LEFT, RIGHT, UP, DOWN;
}
public Segment() {
xpos = 0;
ypos = 0;
dir = Dir.RIGHT;
}
public Segment(int x, int y, Dir d) {
xpos = x;
ypos = y;
dir = d;
}
public Dir getDir() {
return dir;
}
public void setDir(Dir d) {
dir = d;
}
public void setX(int x) {
xpos = x;
repaint();
}
public void setY(int y) {
ypos = y;
repaint();
}
public int getX() {
return xpos;
}
public int getY() {
return ypos;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.setBackground(Color.LIGHT_GRAY);
g.setColor(Color.black);
g.fillRect(xpos, ypos, width, height);
}
}
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Snake extends JFrame implements ActionListener {
Segment segment;
Timer timer = new Timer(50, this);
public Snake() {
setLayout(new BorderLayout());
setSize(500,500);
segment = new Segment(100, 100, Segment.Dir.RIGHT);
segment.setPreferredSize(new Dimension(getWidth(),getHeight()));
add(segment, BorderLayout.CENTER);
timer.start();
setVisible(true);
setTitle("Snake");
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == timer) {
segment.setX((segment.getX() + 4 + getWidth())%getWidth());
}
}
public static void main(String[] arg) {
new Snake();
}
}
Don't override getX() and getY() of the Segment class.
Those are methods of all Swing components. They get the current location of the component within the parent container.
Use different method names to control the location of the painting of your snake. Since your variable names are xPos and yPos maybe use getXPos() and getYPos().
So what happens is that the snake is drawn at xPos/yPos relative to the Segment panel and the Segment panel is also drawn at xPos/yPos relative to its parent container.
So I've been stuck on this problem for a while now and I'm desperate for help. Please help me. I've got 3 classes:
Circle is just suppose to draw a circle in the frame created by Frame with random starting position (and defind the radius).
Frame is the mainclass with methods such as addCircle(), bounce(), start(), stop(), run() (moves the circles) and quit(). This class also creates the frame in which the circles are added to.
Interfa is just for now a inteface frame where I define the radius, number of circles and Frame size.
No matter what I try I cannot add more than two circle (one is colored and one is not):
The "recursive way":
private static void addCircle(int n){
Circle[] circles = new Circle[n+10];
if (n > 0){
circles[circleAdd] = new Circle();
frame.add(circles[circleAdd]);
circleAdd = circleAdd + 1;
addCircle(n-1);
}
}
Normal itterative way
private static void addCircles(int n){
ArrayList<Circle> circles = new ArrayList<Circle>();
for(int i = 0; i<=n;i++){
circles.add(new Circle());
frame.add(circles.get(i));
}
}
This is how I create my Frame:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public Class Frame{
private static JFrame frame;
private static int circleAdd = 0;
private static JPanel fra;
public static void mainFrame(){
frame = new JFrame();
frame.setSize(500,500);
frame.setVisible(true);
fra = new JPanel();
frame.add(fra);
...
//addCircle and addCircles
...
public static void main..
}
}
This is my circle:
import java.awt.*;
import javax.swing.*;
import java.util.Random;
public class Circle extends JPanel{
private Random random = new Random();
public void paint(Graphics g){
int randX = random.nextInt(250)+50;
int randY = random.nextInt(250)+50;
g.drawOval(randX,randY,50,50);
g.setColor(Color.ORANGE);
g.fillOval(100,100,50,50);
}
}
I would suggest that your general approach is wrong. Instead of using a JPanel as the element, you should have a JPanel capable of painting any number of "circles". The Graphics2D API is capable of drawing complex shapes (including ovals).
The main issues I can see are:
JFrame by default is using a BorderLayout, this only allows a single component to be placed in each of the five available positions
Layout managers rely on the preferred/minimum/maximumSize hints to make determinations about the size of the components. They are also responsible for deciding on where the component should be placed. In your current implementation, this would mean that it's possible for you to paint beyond the visible range of the component
Overriding paint is not recommend, and failing to call super.paint could cause a number of unexpected and difficult to diagnose issues
Painting can occur at any time, so using random values in the paint method will cause the UI to constantly change
Instead, you could define your own Circle class which takes the location and size you want and simply acts as a container
public class Circle {
private int x;
private int y;
private int radius;
private Ellipse2D shape;
public Circle(int x, int y, int radius) {
this.x = x;
this.y = y;
this.radius = radius;
this.shape = new Ellipse2D.Double(x, y, radius * 2, radius * 2);
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getRadius() {
return radius;
}
public Rectangle getBounds() {
return shape.getBounds();
}
public void paint(Graphics2D g2d) {
g2d.setColor(Color.ORANGE);
g2d.fill(shape);
}
}
This is simply a container class, it represents the information need to generate the desired outcome. It has a convince method which is capable of then painting the shape itself.
You would then need to create a List of these shapes and paint them to your component
public class TestPane extends JPanel {
private List<Circle> circles = new ArrayList<>(10);
private Dimension size;
public TestPane() {
Random random = new Random();
int maxX = 0;
int maxY = 0;
for (int index = 0; index < 10; index++) {
int randX = random.nextInt(250) + 50;
int randY = random.nextInt(250) + 50;
circles.add(new Circle(randX, randY, 25));
maxX = Math.max(maxX, randX + 50);
maxY = Math.max(maxY, randY + 50);
}
size = new Dimension(maxX, maxY);
}
#Override
public Dimension getPreferredSize() {
return size;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Circle circle : circles) {
Graphics2D g2d = (Graphics2D) g.create();
circle.paint(g2d);
g2d.dispose();
}
}
}
One of the things you seem to lack understanding in is how painting actually works in Swing.
Start by having a look at Performing Custom Painting and Painting in AWT and Swing for more details.
A deeper understanding of how layout managers and the component hierarchy work also wouldn't hurt
This question already has answers here:
How to paint an arrayList of shapes Java
(2 answers)
Closed 7 years ago.
I am tasked with how to paint an arrayList of shapes in java.
I feel i have most of it right however
The final method in ShapeChooserPanel I cannot find out how to print the Shapes in the array, It should paint the current shape at the place the mouse was clicked.
My code is below
Main class:
import javax.swing.JFrame;
public class Lab2 {
public static void main (String[] args) {
JFrame myFrame = new JFrame("Lab 2");
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.add(new ShapeChooserPanel());
myFrame.pack();
myFrame.setVisible(true);
}
}
ShapeChooserPanel
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.ArrayList;
public class ShapeChooserPanel extends JPanel {
private int currentX;
private int currentY;
private Color currentColor;
private int currentShape;
private JButton clearBtn;
private JRadioButton circle, square, triangle, box;
private DrawingPanel drawingPanel;
private JPanel controlsPanel;
//constants representing shape choice
private final int CIRCLE = 0;
private final int SQUARE = 1;
private final int TRIANGLE = 2;
private final int BOX = 3;
//constant delta used for setting distance between points
private final int DELTA = 25;
private int[] Xs;
private int[] Ys;
//store all the shapes to be painted UNCOMMENT when you have Shape.java defined
ArrayList<Shape> shapes;
public ShapeChooserPanel(){
//provide some default values paints a circle at (10,10) in blue
currentX = 10;
currentY = 10;
Xs = new int[4];//we will use all 4 points for the square, but only the first 3 for the triangle
Ys = new int[4];
setPoints(currentX,currentY);
currentShape = CIRCLE;
currentColor = Color.red;
shapes = new ArrayList<Shape>();
//instantiate the controls panel and set its layout to display everything in a single column
controlsPanel = new JPanel();
controlsPanel.setLayout(new BoxLayout(controlsPanel, BoxLayout.Y_AXIS));
//TODO: add clear button *
// TODO: define radio buttons *
clearBtn = new JButton("Clear");
clearBtn.addActionListener(new ClearListener());
circle = new JRadioButton("Red Circle");
circle.addActionListener(new ShapeListener());
square = new JRadioButton("Cyan Square");
square.addActionListener(new ShapeListener());
triangle = new JRadioButton("Green Triangle");
triangle.addActionListener(new ShapeListener());
box = new JRadioButton("Blue Box");
box.addActionListener(new ShapeListener());
ButtonGroup group = new ButtonGroup();
group.add(clearBtn);
group.add(circle);
group.add(square);
group.add(triangle);
group.add(box);
controlsPanel.add(clearBtn);
controlsPanel.add(circle);
controlsPanel.add(square);
controlsPanel.add(triangle);
controlsPanel.add(box);
//TODO: add radio buttons to group *
//TODO add listeners to radio buttons *
//TODO: add radio buttons to controls panel *
drawingPanel = new DrawingPanel();
drawingPanel.setBorder(BorderFactory.createLineBorder(Color.black));
//TODO: set a border around the drawing panel *
drawingPanel.setPreferredSize(new Dimension(200,200));
drawingPanel.addMouseListener(new PanelListener());
add(drawingPanel);
add(controlsPanel);
setPreferredSize(new Dimension (300,400));
}//end constructor
public void setPoints(int x, int y) {
//TODO: set Xs and Ys *
for(int i = 0; i < 4; i++) {
Xs[i] = x;
Ys[i] = y;
}
}
private class ClearListener implements ActionListener{
public void actionPerformed(ActionEvent ae){
shapes.removeAll(shapes);
drawingPanel.repaint();
}
}
private class PanelListener implements MouseListener {
public void mouseClicked(MouseEvent me) {
currentX = me.getX();
currentY = me.getY();
//TODO: find coordinates of this mouse click *
//TODO: add a new shape to the shapes list*
shapes.add(new Shape(currentX, currentY, SQUARE,Color.cyan));
setPoints(currentX, currentY);
//TODO: call setPoints with current x and y values *
drawingPanel.repaint();
}
public void mouseExited(MouseEvent me){}
public void mouseEntered(MouseEvent me){}
public void mouseReleased(MouseEvent me){}
public void mousePressed(MouseEvent me){}
}
//Class to listen for radio button changes
private class ShapeListener implements ActionListener{
public void actionPerformed(ActionEvent me){
//TODO: determine which radio button was clicked *
if(me.getActionCommand().equals("Red Circle")){
shapes.add(new Shape(currentX, currentY, CIRCLE, Color.red));
}
if (me.getActionCommand().equals("Cyan Square")){
shapes.add(new Shape(currentX, currentY, SQUARE, Color.cyan));
}
if(me.getActionCommand().equals("Green Triangle")){
shapes.add(new Shape(currentX, currentY, TRIANGLE, Color.green));
}
if(me.getActionCommand().equals("Blue Box")){
shapes.add(new Shape(currentX, currentY, BOX, Color.blue));
}
//TODO: set current shape and color *
drawingPanel.repaint();
}
}
private class DrawingPanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
//TODO: paint all the shapes in our list
}
}
}
And Shape.java
import java.awt.Color;
import java.awt.Graphics;
public class Shape {
private int x,y;
private int type;
private Color c;
public Shape(int x, int y, int type, Color c) {
this.x = x;
this.y = y;
this.type = type;
this.c = c;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getType() {
return type;
}
public Color getColor() {
return c;
}
}
In your DrawingPanel class you need a method like addShape(...) which will add a Shape object to the ArrayList. Then in the paintComponent(...) method you iterate through the ArrayList to paint each shape in the list.
Check out the Draw On Component example found in Custom Painting Approaches for a working example.
The example only draws Rectangles so your code will be a little more involved as you will need to check which type of shape you want to paint.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
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:
How can I switch between drawing a circle and a rectangle without the circle appearing inside the rectangle?
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?
Why doesn't it work drawing by free hand?
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;
}
}
There are a multitude of things going on...
Overriding paint of JFrame
Not calling super.paint before performing custom painting.
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...