Related
I created an application which allows instantiating shapes which can be circle rectangle or anything and used sorting technique (Bubble Sort) to sort the six shapes based on interfaces.
The problem is I am not familiar with Design patterns and what patterns are being used- I am new so i followed youtube videos and played around with it and it worked.
I have 1 main class where I have
MAIN :
public class Main {
public static void main(String[] args) {
JButton btnLoadShapes, btnSortShapes;
btnLoadShapes = new JButton("Load Shapes");
btnLoadShapes.setBounds(150, 10, 150, 30);
btnSortShapes = new JButton("Sort Shapes");
btnSortShapes.setBounds(310, 10, 150, 30);
JPanel panelShapes = new JPanel() {
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
Shape shape = new Shape();
g2d.setColor(Color.RED);
Square s = shape.getS();
g2d.fillRect(s.getX(),s.getY(),s.getWidth(), s.getHeight());
g2d.setColor(Color.BLUE);
Circle c = shape.getC();
g2d.fillOval(c.getX(),c.getY(), c.getWidth(), c.getHeight());
g2d.setColor(new Color(131, 21, 1));
Rectangle r = shape.getR();
g2d.fillRect(r.getX(), r.getY(), r.getWidth(), r.getHeight());
g2d.setColor(Color.PINK);
Circle C1 = shape.getC1();
g2d.fillOval(C1.getX(),C1.getY(), C1.getWidth(), C1.getHeight());
g2d.setColor(Color.green);
Square S1 = shape.getS1();
g2d.fillRect(S1.getX(),S1.getY(),S1.getWidth(), S1.getHeight());
g2d.setColor(Color.magenta);
Rectangle r2 = shape.getR2();
g2d.fillRect(r2.getX(),r2.getY(),r2.getWidth(), r2.getHeight());
}
};
panelShapes.setBounds(10, 50, 560, 500);
panelShapes.setBorder(BorderFactory.createLineBorder(Color.BLACK));
panelShapes.setVisible(false);
btnLoadShapes.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
panelShapes.setVisible(true);
}
});
and i have created 3 different classes Circle Rectangle and Square - from which i call for rectangle = shape.getC();
For example
public class Circle {
private int x, y, width, height;
public Circle(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
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;
}
}
Now the lastly I created 2 more classes called Shape and Sorting
where in shape i intiated public shape s = new Square with dimensions and set getters and setters and lastly i used sorting technique.
Can someone help me understand what are the design patterns that are being used here?
( I assume that Factory method is being used = since i defined main (* interface) and created subclasses ( Shapes rectangle circle) to instantiate.
Sorry if i sound out the place- I am just trying to understand and learn it.
A "factory" creates "something", the important thing in this context is, you don't care "how" it's created, only that it conforms to the specified type.
For example, you have a ShapeFactory which can create different shapes, you don't care "how" those shapes are defined or implemented, only that they conform to the notion of a "shape"
So, lets start with a basic concept...
public interface Shape {
public void paint(Graphics2D g2d);
}
This just defines a basic concept and states that it can be painted.
Next, we need something to create those shapes...
public class ShapeFactory {
enum ShapeType {
CIRCLE, RECTANGE, SQUARE;
}
public static Shape create(ShapeType type, int x, int y, int width, int height, Color storkeColor, Color fillColor) {
return null;
}
}
Ok, as it stands, that's pretty boring, it's only ever going to return null right now, but this gives us a basic contract.
"Please factory, create me shape of the specified type, within the specified bounds, with the specified colors"
Now, as I said, the implementation is unimportant, to the caller, and we could have a dynamic factory which could delegate the creation to other factories which could create shapes differently based on a wide ranging set of needs ... but that's getting ahead of ourselves.
Let's go about creating some actual shapes...
public abstract class AbstractShape implements Shape {
private int x;
private int y;
private int width;
private int height;
private Color storkeColor;
private Color fillColor;
public AbstractShape(Color storkeColor, Color fillColor) {
this.storkeColor = storkeColor;
this.fillColor = fillColor;
}
public AbstractShape(int x, int y, int width, int height, Color storkeColor, Color fillColor) {
this(storkeColor, fillColor);
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
protected void setX(int x) {
this.x = x;
}
protected void setY(int y) {
this.y = y;
}
protected void setWidth(int width) {
this.width = width;
}
protected void setHeight(int height) {
this.height = height;
}
public Color getStorkeColor() {
return storkeColor;
}
public Color getFillColor() {
return fillColor;
}
#Override
public void paint(Graphics2D g2d) {
Graphics2D g = (Graphics2D) g2d.create();
Color storkeColor = getStorkeColor();
Color fillColor = getFillColor();
if (fillColor != null) {
g.setColor(fillColor);
paintFilled(g);
}
if (storkeColor != null) {
g.setColor(storkeColor);
paintStroked(g);
}
g.dispose();
}
abstract protected void paintFilled(Graphics2D g2d);
abstract protected void paintStroked(Graphics2D g2d);
}
public class CircleShape extends AbstractShape {
public CircleShape(int x, int y, int width, int height, Color storkeColor, Color fillColor) {
super(storkeColor, fillColor);
int size = Math.min(width, height);
x = x + ((width - size) / 2);
y = y + ((height - size) / 2);
setX(x);
setY(y);
setWidth(size);
setHeight(size);
}
#Override
protected void paintFilled(Graphics2D g2d) {
g2d.fillOval(getX(), getY(), getWidth(), getHeight());
}
#Override
protected void paintStroked(Graphics2D g2d) {
g2d.drawOval(getX(), getY(), getWidth(), getHeight());
}
}
public class SquareShape extends AbstractShape {
public SquareShape(int x, int y, int width, int height, Color storkeColor, Color fillColor) {
super(storkeColor, fillColor);
int size = Math.min(width, height);
x = x + ((width - size) / 2);
y = y + ((height - size) / 2);
setX(x);
setY(y);
setWidth(size);
setHeight(size);
}
#Override
protected void paintFilled(Graphics2D g2d) {
g2d.fillRect(getX(), getY(), getWidth(), getHeight());
}
#Override
protected void paintStroked(Graphics2D g2d) {
g2d.drawRect(getX(), getY(), getWidth(), getHeight());
}
}
public class RectagleShape extends AbstractShape {
public RectagleShape(int x, int y, int width, int height, Color storkeColor, Color fillColor) {
super(x, y, width, height, storkeColor, fillColor);
}
#Override
protected void paintFilled(Graphics2D g2d) {
g2d.fillRect(getX(), getY(), getWidth(), getHeight());
}
#Override
protected void paintStroked(Graphics2D g2d) {
g2d.drawRect(getX(), getY(), getWidth(), getHeight());
}
}
I always like a abstract class to carry the "common" functionality and to help make life a little simpler.
The important thing here is to note that both CircleShape and SquareShape, by their nature are, well, square (they have equal width and height). So, in this implementation, I define them to fit within the middle of the specified bounds - this is a "implementation" detail.
"But isn't that what I'm doing you?" you ask. Well, no, not really. When you call shape.getS(), for example, it's return a concrete class, which I assume has the same properties as the last object created by it, otherwise it will move all over the place.
Instead, what I'm doing is allowing you to define the properties you want the shape to have and then making it.
You want a cake? Sure, pass me the ingredients and I'll make you a cake, you still end up with a cake, but depending on the ingredients it's a different "type" of cake.
So, based on the above, we could do something like...
public class TestPane extends JPanel {
private List<Shape> shapes = new ArrayList<>(25);
public TestPane() {
shapes.add(ShapeFactory.create(ShapeFactory.ShapeType.CIRCLE, 10, 10, 200, 100, Color.RED, Color.BLUE));
shapes.add(ShapeFactory.create(ShapeFactory.ShapeType.RECTANGE, 10, 120, 200, 100, Color.BLUE, Color.GREEN));
shapes.add(ShapeFactory.create(ShapeFactory.ShapeType.SQUARE, 10, 240, 200, 100, Color.GREEN, Color.YELLOW));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(220, 350);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Shape shape : shapes) {
shape.paint(g2d);
}
g2d.dispose();
}
}
I could have made the ShapeFactory with dedicated createCircle, createRectangle and createSquare methods, I could have had them return interfaces of Circle, Square and Rectangle (and I would have based those of Shape because I'm like that) and it would still be a factory.
One of things to keep in mind is, a "factory" should be implementation independent. I should be able to make use of "different" shape factories to get different effects, but at the end of the day, they'd still just be generating Shapes
Remember, a factory will take something and it will create something from it.
im trying to draw 20 rectangles on screen at once using two different classes, one for the frame and one for each rectangle, However only 1 is being drawn. here is the window class
private final List<Vehicle> vehicles = new ArrayList<>();
public Window() {
this.initWindow();
this.populateVehicles();
}
private void initWindow() {
this.setSize(1440, 920);
this.setResizable(false);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setTitle("Steering");
this.setVisible(true);
}
public void populateVehicles() {
for (int i = 0; i < 20; i++) {
int x = Util.getRandomInt(10, 1000);
int y = Util.getRandomInt(20, 800);
Vehicle v = new Vehicle(x, y);
this.add(v);
vehicles.add(v);
System.out.print("printing new rect # " + x + " : " + y + "\n");
}
}
private void repopulateVehicles() {
if (this.vehicles.isEmpty()) return;
for (Vehicle v : vehicles) {
v.repaint();
}
}
public void refresh(boolean hard) {
this.repopulateVehicles();
if (hard) {
SwingUtilities.updateComponentTreeUI(this);
}
}
}
and here is the rectangle class
public class Vehicle extends JPanel {
private int x;
private int y;
private int speed;
private final int width;
private final int height;
public Vehicle(int x, int y) {
this.x = x;
this.y = y;
this.width = 25;
this.height = 10;
}
public void moveVehicle(int x_, int y_) {
this.x += x_;
this.y += y_;
this.repaint(x, y, x_, y_);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
Color c = new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255));
g2d.setColor(c);
g2d.rotate(Math.toRadians(30));
g2d.fillRect(this.x, this.y, this.width, this.height);
}
public Dimension getPreferredSize() {
return new Dimension(this.width, this.height);
}
}
the output is just 1 rectangle being drawn on the frame, while is should be that 20 rectangles are being drawn. the debug print statement shows that each is being drawn at different coords but they arent being shown.
The default layout manager for the content pane of a JFrame is the BorderLayout.
By default each component is added to the CENTER of the BorderLayout. Only the last component added will be given a size/location. So only the last component will be visible.
The solution is:
to display components at random locations you will need to use a null layout on the content pane.
you will need to set the size of each component equal to the preferred size, otherwise the default size will still be (0, 0) and there is nothing to paint.
you will need to set the size of each location, otherwise the default will be (0, 0)
the "random" color of the Vehicle should be assigned in the constructor, not the painting method. A painting method should only paint the current state, not change the state of the component.
Note:
This answer only addresses the issues of using a JPanel to represent your Vehicle. You need to understand how a layout manager works to give components a size/location. In this case because you are randomly positioning components on the panel you would need to use a null layout and set the size/location yourself.
This is not the preferred approach. The better approach is to do custom painting yourself of all the Vehicles as demonstrated by Gilbert.
I rearranged your code to create this GUI. I shrank it down to fit better in the answer.
It's a really good idea to separate your code into a model / view / controller (MVC) pattern. This allows you to separate your concerns and focus on one part of the application at a time.
I made your Vehicle class a plain Java getter / setter class that holds one vehicle.
I created a Roadway class to hold the List of Vehicle instances. The Roadway class is another plain Java getter / setter class.
I renamed your Window class DrawingPanel and made it a drawing JPanel. The DrawingPanel class just draws the List of Vehicle instances from the Roadway class. Period.
I created a VehicleListener class to move the vehicles.
I put the JFrame code into a separate class that calls all the other classes.
I made all the classes inner classes so I could post the code as one block.
Here's the complete runnable code.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class VehicleGUI implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new VehicleGUI());
}
private final Dimension drawingPanelDimension;
private DrawingPanel drawingPanel;
private final Roadway roadway;
public VehicleGUI() {
this.drawingPanelDimension = new Dimension(400, 200);
this.roadway = new Roadway(drawingPanelDimension);
}
#Override
public void run() {
JFrame frame = new JFrame("Steering");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.drawingPanel = new DrawingPanel(roadway,
drawingPanelDimension);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
Timer timer = new Timer (100, new VehicleListener(this, roadway));
timer.setInitialDelay(3000);
timer.start();
}
public void repaint() {
drawingPanel.repaint();
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private Roadway roadway;
public DrawingPanel(Roadway roadway,
Dimension drawingPanelDimension) {
this.roadway = roadway;
this.setBackground(Color.WHITE);
this.setPreferredSize(drawingPanelDimension);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (Vehicle vehicle : roadway.getVehicles()) {
g2d.setColor(vehicle.getColor());
g2d.fillRect(vehicle.getX(), vehicle.getY(),
vehicle.getWidth(), vehicle.getHeight());
}
}
}
public class VehicleListener implements ActionListener {
private final Roadway roadway;
private final VehicleGUI frame;
public VehicleListener(VehicleGUI frame, Roadway roadway) {
this.frame = frame;
this.roadway = roadway;
}
#Override
public void actionPerformed(ActionEvent event) {
for (Vehicle vehicle : roadway.getVehicles()) {
vehicle.moveVehicle();
}
frame.repaint();
}
}
public class Roadway {
private final Dimension drawingPanelDimension;
private final List<Vehicle> vehicles;
private final Random random;
public Roadway(Dimension drawingPanelDimension) {
this.drawingPanelDimension = drawingPanelDimension;
this.vehicles = new ArrayList<>();
this.random = new Random();
populateVehicles();
}
private void populateVehicles() {
int width = drawingPanelDimension.width;
int height = drawingPanelDimension.height;
for (int i = 0; i < 20; i++) {
int x = random.nextInt(width - 40) + 20;
int y = random.nextInt(height - 40) + 20;
Vehicle v = new Vehicle(x, y, drawingPanelDimension);
vehicles.add(v);
}
}
public List<Vehicle> getVehicles() {
return vehicles;
}
}
public class Vehicle {
private double x;
private double y;
private final int width;
private final int height;
private double speed;
private final Color color;
private final Dimension drawingPanelDimension;
public Vehicle(int x, int y, Dimension drawingPanelDimension) {
this.x = x;
this.y = y;
this.width = 25;
this.height = 10;
this.drawingPanelDimension = drawingPanelDimension;
this.speed = Math.random() * 20.0 - 10.0;
int red = (int) (Math.random() * 128.0);
int green = (int) (Math.random() * 128.0);
int blue = (int) (Math.random() * 128.0);
this.color = new Color(red, green, blue);
}
public void moveVehicle() {
this.x += speed;
this.x = (this.x < 0) ? drawingPanelDimension.width + this.x : this.x;
this.x = (this.x > drawingPanelDimension.width) ?
this.x - drawingPanelDimension.width : this.x;
}
public int getX() {
return (int) Math.round(x);
}
public int getY() {
return (int) Math.round(y);
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public Color getColor() {
return color;
}
}
}
I have a java application that i have been coding, it is an application that can allow me to draw shapes like a rectangle. My application can draw shapes but I cannot save them because when i try to draw a new shape and I click somewhere else the previously drawn shape disappears and is replaced by a new one. I tried array list to save my shapes but it does not work.
here is my code:
public class Toile extends JPanel {
Vector<Forme> forme = new Vector<Forme>();
private Color couleur;
private int x;
private int y;
private int x2;
private int y2;
private Oval oval;
private Rectangl rect;
public Toile(){
initComponents();
}
public void initComponents(){
addMouseListener(new java.awt.event.MouseAdapter() {
public void mousePressed(java.awt.event.MouseEvent evt) {
formMousePressed(evt); }
public void mouseReleased(java.awt.event.MouseEvent evt) {
formMouseReleased(evt); } });
addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
public void mouseDragged(java.awt.event.MouseEvent evt) {
formMouseDragged(evt); } });
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(couleur);
drawfillRect(g, x, y, x2, y2);
}
public void setStartPoint(int x, int y) {
this.x = x;
this.y = y;
}
public void setEndPoint(int x, int y) {
x2 = (x);
y2 = (y);
}
public void drawfillRect(Graphics g, int x, int y, int x2, int y2) {
int px = Math.min(x,x2);
int py = Math.min(y,y2);
int pw=Math.abs(x-x2);
int ph=Math.abs(y-y2);
//g.fillRect(px, py, pw, ph);
Rectangl rect = new Rectangl(px,y,x2,py,pw,ph,couleur,true);
rect.dessinerfrect(g);
forme.add(rect);
}
}
private void formMousePressed(java.awt.event.MouseEvent evt) {
setStartPoint(evt.getX(), evt.getY());
repaint();
}
private void formMouseReleased(java.awt.event.MouseEvent evt) {{
setEndPoint(evt.getX(), evt.getY());
repaint();
//dessiner(this.getGraphics());
}
}
private void formMouseDragged(java.awt.event.MouseEvent evt){{
setEndPoint(evt.getX(), evt.getY());
repaint();
//dessiner(this.getGraphics());
}
}
As you can see this is the class that does the drawings, the rectangle that will be drawn is an object from a class that I created and this class is a subclass of a super class Forme. As I said previously, the application can draw shapes but the shapes that are drawn are not saved. Also I removed the getters and setters from my post because I wanted to keep only what was essential and I wanted to make my post clearer.
Here is the class Rectangl:
public class Rectangl extends Forme {
private int largeur;
private int hauteur;
private Rectangle rectangle;
public Rectangl(int x1,int y1, int x2 ,int y2,int largeur,int hauteur,Color couleur,Boolean plein){
super(x1,y1,x2,y2,couleur,plein);
this.largeur = largeur;
this.hauteur = hauteur;
}
public void dessinerrect(Graphics g){
g.setColor(couleur);
g.drawRect((int)point1.getX(), (int)point2.getY(), largeur, hauteur);
}
public void dessinerfrect(Graphics g){
g.setColor(couleur);
g.fillRect((int)point1.getX(), (int)point2.getY(), largeur, hauteur);
}
}
You'll need to implement a display list. This is a data structure that represents all the items currently in the drawing. The component painter just traverses the list and draws each item (usually after erasing the screen so that deleted objects don't appear in the new drawing). It also optionally draws the "rubberband cursor" if the mouse has been pressed and not yet released. The mouse actions (usually releasing the mouse button just modify status variables including display list (add, select, delete, etc.) and then repaint the drawing surface so the component painter is called.
A Java ArrayList is a reasonable way to implement a simple display list. The items themselves are a classical use of interfaces and/or inheritance:
interface DisplayListItem {
void draw(Graphics g);
}
abstract class AbstractRectangle implements DisplayListItem {
protected int x, y, w, h;
protected Color color;
Rectangle(int x, int y, int w, int h, Color color) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.color = color;
}
}
class Rectangle extends AbstractRectangle {
Rectangle(int x, int y, int w, int h, Color color) {
super(x, y, w, h, color);
}
#Override
void draw(Graphic g) {
g.setColor(color);
g.drawRect(x, y, w, h);
}
}
class FilledRectangle extends AbstractRectangle {
FilledRectangle(int x, int y, int w, int h, Color color) {
super(x, y, w, h, color);
}
#Override
void draw(Graphic g) {
g.setColor(color);
g.fillRect(x, y, w, h);
}
}
private List<DisplayListItem> displayList = new ArrayList<>();
private int xPress, yPress, xDrag, yDrag;
private boolean mousePressed = false;
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (DisplayListItem item : displayList) {
item.draw(g);
}
if (mousePressed) {
// Draw the click-and-drag cursor.
g.setColor(Color.RED);
g.drawRect(xPress, yPress, xDrag - xPress, yDrag - yPress);
}
}
private void formMousePressed(MouseEvent evt) {
xPress = evt.getX();
yPress = evt.getY();
mousePressed = true;
}
private void formMouseDragged(MouseEvent evt) {
if (!mousePressed) return;
xDrag = evt.getX();
yDrag = evt.getY();
repaint();
}
private void formMouseReleased(MouseEvent evt) {
if (!mousePressed) return;
xDrag = evt.getX();
yDrag = evt.getY();
// Adding a rectangle to the display list makes it permanent.
displayList.add(new Rectangle(xPress, yPress, xDrag - xPress, yDrag - yPress));
mousePressed = false;
repaint();
}
Caveat: This is uncompiled, untested code. Use at your own risk.
Swing has the useful Shape interface which has been implemented into several concrete classes that could serve your purpose well.
There are several different Shapes (Ellipse2D, Rectangle2D, RoundRectangle2D) are stored in an ArrayList of Shape objects, and then these are drawn within the JPanel's paintComponent method after first casting the Graphics object into a Graphics2D.
I am tasked with how to paint an arrayList of shapes in java.
I feel i have most of it right, there are two methods i am confused about however
the method in ShapeChooserPanel called public void setPoints(int x, int y)
Since Xs and Ys are arrays, how would i set the x and y values?
And the other is one is 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 class
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 *
Xs[0] = x;
Ys[0] = 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.addAll(shapes);
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.getSource() == circle){
currentShape = CIRCLE;
currentColor = Color.red;
}
if(me.getSource() == square){
currentShape = SQUARE;
currentColor = Color.cyan;
}
if(me.getSource() == triangle){
currentShape = TRIANGLE;
currentColor = Color.green;
}
if(me.getSource() == box){
currentShape = BOX;
currentColor = 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 my Shape class
import java.awt.Color;
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 x(int x) {
return x;
}
public int y(int y) {
return y;
}
public int type(int type) {
return type;
}
public Color c(Color c) {
return c;
}
}
super.paintComponent(g) invokes the paintComponent method from the superclass of JPanel (the JComponent class) to erase whatever is currently drawn on the panel. This is useful for animation, but not for setting color.
I guess you'll have to set the color of the shapes in your Arraylist<Shape> shapes when you will create them, nonetheless it could be helpfull to see the Shape class. Maybe you could create a function changeColor(Color ColorToBeSet) in this shape class and loop through the shapes ArrayList to call it at the end of your ShapeListener
You could...
Define an "abstract" concept of a shape, which has the basic properties (location and size) and which can paint itself...
public abstract class Shape {
private int x, y;
private int width, height;
private Color c;
public Shape(int x, int y, int width, int height, Color c) {
this.x = x;
this.y = y;
this.c = c;
this.width = width;
this.height = height;
}
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Color getColor() {
return c;
}
public abstract void paint(Graphics2D g2d);
}
Then you can implement the individual shapes...
public class Rectangle extends Shape {
public Rectangle(int x, int y, int width, int height, Color c) {
super(x, y, width, height, c);
}
#Override
public void paint(Graphics2D g2d) {
g2d.setColor(getColor());
g2d.drawRect(getX(), getY(), getWidth(), getHeight());
}
}
public class Oval extends Shape {
public Oval(int x, int y, int width, int height, Color c) {
super(x, y, width, height, c);
}
#Override
public void paint(Graphics2D g2d) {
g2d.setColor(getColor());
g2d.drawOval(getX(), getY(), getWidth(), getHeight());
}
}
Then, you can simply call the paint method of each instance of Shape as required...
public class TestPane extends JPanel {
private List<Shape> shapes;
public TestPane() {
shapes = new ArrayList<>(25);
shapes.add(new Rectangle(10, 10, 20, 20, Color.RED));
shapes.add(new Oval(15, 15, 40, 20, Color.RED));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Shape shape : shapes) {
Graphics2D g2d = (Graphics2D) g.create();
shape.paint(g2d);
g2d.dispose();
}
}
}
So what I want to achieve, is to create a simple program that lets the user create rectangles on the screen and then move them around.
I know that I can declare a new object in the code (i.e rectangle i = new rectangle(x,y,sizex,sizey)) however that will create only one, moreover it forces me to declare it in the code:
block1 = new block
block2 = new block
etc
Question is: How can I let the user create infinite rectangles with the use of lets say a button (not necessarily a button, it can be anything) and then let the user be capable of modyfing them (location/size etc).
Examples would be nice. I just feel there is a better way than declaring a gazillion objects in the code and then displaying them one by one. In C++ i could declare a malloc expandable container that would just hold the values, and then just display things using these values, not sure for java.
Simple code for reference:
public static void main(String[] args){
JFrame frame = new JFrame("A.L.T.E.S");
//Container container = frame.getContentPane();
frame.setSize(1024, 768);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Block object = new Block(10,10,20,20);
frame.add(object);
object.reDraw();
}
public class Block extends JPanel{
int yPos;
int xPos;
int xSize;
int ySize;
public Block(int xPos, int yPos, int xSize, int ySize){
this.xPos = xPos;
this.yPos = yPos;
this.xSize = xSize;
this.ySize = ySize;
}
public void setYPos(int yPos){
this.yPos = yPos;
}
public void setXPos(int xPos){
this.xPos = xPos;
}
public void setXSize(int xSize){
this.xSize = xSize;
}
public void setYSize(int ySize){
this.ySize = ySize;
}
public int getYPos(){
return yPos;
}
public int getXPos(){
return xPos;
}
public int getYSize(){
return ySize;
}
public int getXSize(){
return xSize;
}
public void reDraw(){
repaint();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillRect(xPos, yPos, xSize, ySize);
}
}
Nothing in Java is simple.
First, Java already has a Rectangle class that holds the origin and size of a Rectangle. In your code, you're making all the rectangles blue. Suppose you want the user to set the color of a Rectangle. You can define your Block class like this.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
public class Block {
private Color color;
private Rectangle rectangle;
public Block(int x, int y, int width, int height) {
this(new Rectangle(x, y, width, height));
}
public Block(Rectangle rectangle) {
this.rectangle = rectangle;
this.color = Color.BLUE;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public Rectangle getRectangle() {
return rectangle;
}
public void draw(Graphics g) {
g.setColor(getColor());
g.fillRect(rectangle.x, rectangle.y,
rectangle.width, rectangle.height);
}
}
Next, you need a model class to hold all of the Blocks that your user defines.
Something like this BlockList class.
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;
public class BlockList {
private List<Block> blockList;
public BlockList() {
this.blockList = new ArrayList<Block>();
}
public void init() {
this.blockList.clear();
}
public void addBlock(Block block) {
this.blockList.add(block);
}
public void draw(Graphics g) {
for (int i = 0; i < blockList.size(); i++) {
blockList.get(i).draw(g);
}
}
}
Now that you have your GUI model defined, you would build your GUI. Your drawing JPanel would call the draw method in your BlockList class.
I strongly suggest that you go through the Oracle tutorial on Swing. Go through the complete tutorial before you attempt to create a Swing GUI.
In java, use a Collection. In this case, use a List. You can create an ArrayList<Block>, then invoke add to add new rectangles. Then, you can iterate through this collection by using iterator, then calling hasNext and next on the iterator to find all the elements. I'm sure this brief hypnosis is going to be confusing, and if so, check out the Java Tutorial on Collections, which explains everything in all the detail you are going to need.