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();
}
}
}
Related
I am in the process of implementing a simple graph editor. It should be possible to create any Node (Sphere object) by mouse click and move it by drag and move. It works as far as it goes, but it behaves very strangely. When I click on the node, it slides away from the cursor. The hitbox from every Sphere object is strange too, but I can't explain why. Is there a better solution for my code?
This behavior
public class EditorPanelView extends Panel
{
private static EditorPanelView instance = null;
private static GraphController graphController = GraphController.getInstance();
private EditorPanelView(Color color, int x, int y, int width, int height) {
super(new BorderLayout(), color, x, y, width, height);
addMouseAdapter();
}
private void addMouseAdapter() {
MouseAdapter mouseAdapter = new MouseAdapter() {
private Sphere sphere;
#Override
public void mousePressed(MouseEvent e) {
if (getComponentAt(e.getX(), e.getY()) instanceof Sphere) {
sphere = (Sphere) getComponentAt(e.getPoint());
}
}
#Override
public void mouseClicked(MouseEvent e) {
Sphere sphere = new Sphere(e.getX(), e.getY());
add(sphere);
System.out.println("MOUSE CLICKED ON EDITORPANEL; SPHERE CREATED!");
revalidate();
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
Point p2 = e.getPoint();
Point loc = sphere.getLocation();
loc.translate(p2.x - sphere.getX(), p2.y - sphere.getY());
sphere.setLocation(loc);
}
#Override
public void mouseReleased(MouseEvent e) {
sphere = null;
System.out.println("Mouse released!");
}
};
addMouseListener(mouseAdapter);
addMouseMotionListener(mouseAdapter);
}
public static EditorPanelView getInstance(Color color, int x, int y, int width, int height) {
return Objects.requireNonNullElseGet(instance, () -> new EditorPanelView(color, x, y, width, height));
}
}
public class Sphere extends JComponent{
private int positionX;
private int positionY;
private final int width = 50;
private final int height = 50;
public Sphere(int x, int y) {
this.positionX = x;
this.positionY = y;
}
#Override
public void paintComponent(Graphics graphics) {
Graphics2D sphere = (Graphics2D) graphics;
sphere.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
sphere.setPaint(Color.BLACK);
sphere.fillOval(positionX -(width/2), positionY -(height/2), width, height);
}
}
public class FrameView extends JFrame {
private static FrameView instance = null;
private static final Color FRAMECOLOR = Color.darkGray;
private FrameView() {
Dimension size = Toolkit.getDefaultToolkit().getScreenSize();
GraphController graphController = GraphController.getInstance();
getContentPane().setBackground(FRAMECOLOR);
setTitle("Graph Editor");
setSize((int) size.getWidth()-200, (int) size.getHeight()-200);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setResizable(false);
getContentPane().setLayout(new GroupLayout(getContentPane()));
setJMenuBar(MenuBarView.getInstance());
add(EditorPanelView.getInstance(Color.LIGHT_GRAY, 260, 53, getWidth()-270, getHeight()-113));
add(OverviewPanelView.getInstance(Color.gray, 10, 53, 240, getHeight()-113));
add(ButtonPanelView.getInstance(FRAMECOLOR, 5, 5, getWidth()-10, 42));
}
public static JFrame getInstance() {
return Objects.requireNonNullElseGet(instance, FrameView::new);
}
}
public class Panel extends JPanel {
LayoutManager layout;
public Panel(LayoutManager layout, Color color, int x, int y, int width, int height) {
this.layout = layout;
setLayout(this.layout);
setBounds(x, y, width, height);
setBackground(color);
}
}
I tried to change the x and y coordinates in EditorPanelView::addMouseAdapter()
I'm writing a program that displays a circle every time you click the Jpanel. I have it all set up and I want to be able to use the drawCircle method I created in my circle class to draw the circles in the paintComponent method. I'm storing all of the circles created in a linked list. Then I interate through each Circle in the list and try to use the method in my Circle class called drawCircle().
For some reason, if I try to use c1.drawCircle() in a for loop in the My panel class it only draws the last circle that was created. But if I just use g.fillOval(with the correct parameters grabbing the values from the Circle class) in the for loop it works properly and displays all the circles. Why is it doing this and how do I go about using the method in the Circle class properly
I'm unsure what to try right now.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.LinkedList;
public class MouseTest {
private int borderWidth = 20;
private JFrame frame;
private boolean tracking;
private boolean start;
private boolean clearBol;
private int xstart;
private int ystart;
private int xend;
private int yend;
private LinkedList<Circle> circles;
public MouseTest() {
tracking = false;
start = false;
circles = new LinkedList<Circle>();
frame = new JFrame();
frame.setBounds(250, 98, 600, 480);
frame.setTitle("Window number three");
Container cp = frame.getContentPane();
JButton clear = new JButton("Clear");
JToggleButton circleButton = new JToggleButton()("Circles");
JToggleButton drawButton = new JToggleButton("Draw");
ButtonGroup circleOrDraw = new ButtonGroup();
MyPanel pane = new MyPanel();
clear.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
clearBol = true;
frame.repaint();
}
});
JPanel top = new JPanel();
top.setLayout(new FlowLayout());
top.add(clear);
circleOrDraw.add(circleButton);
circleOrDraw.add(drawButton);
top.add(circleOrDraw);
cp.add(top, BorderLayout.NORTH);
cp.add(pane, BorderLayout.CENTER);
pane.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
xstart = e.getX();
ystart = e.getY();
start = false;
}
public void mouseReleased(MouseEvent e) {
xend = e.getX();
yend = e.getY();
if (xend < xstart) {
int tmp = xstart;
xstart = xend;
xend = tmp;
}
if (yend < ystart) {
int tmp = ystart;
ystart = yend;
yend = tmp;
}
start = true;
frame.repaint();
}
});
pane.addMouseMotionListener(new MouseMotionAdapter() {
public void mouseMoved(MouseEvent e) {
if (tracking) {
int x = e.getX();
int y = e.getY();
msg("(" + x + ", " + y + ")");
}
}
});
frame.setVisible(true);
} // constructor
public static void main(String[] arg) {
MouseTest first = new MouseTest();
} // main
public void msg(String s) {
System.out.println(s);
}
public void trackMouse() {
tracking = !tracking;
} // trackMouse
public class Circle extends JPanel {
Graphics g;
int x;
int y;
int r;
Color color;
public Circle(Graphics g, int x, int y, int r) {
this.g = g;
this.x = x;
this.y = y;
this.r = r;
int red = (int) (256 * Math.random());
int green = (int) (256 * Math.random());
int blue = (int) (256 * Math.random());
this.color = new Color(red, green, blue);
}
public void drawCircle() {
int x2 = x - (r / 2);
int y2 = y - (this.r / 2);
g.setColor(color);
g.fillOval(x2, y2, this.r, this.r);
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Color getColor() {
return color;
}
public int getR() {
return r;
}
}
public class MyPanel extends JPanel {
public void paintComponent(Graphics g) {
if (start) {
circles.add(new Circle(g, xend, yend,
(int) ((250 * Math.random() + 4))));
//Area where I'm having issues
for (Circle c1 : circles) {
msg("" + c1.getX());
// this method that I created in the circle class will only draw the first circle
//c1.drawCircle();
int r = c1.getR();
int x = c1.getX();
int y = c1.getY();
g.setColor(c1.getColor());
g.fillOval((c1.getX() - (r / 2)), (c1.getY() - (r / 2)),
r, r); // this will display all the circles
}
int size = circles.size();
msg(size + " Size");
msg("" + circles.getLast().getX());
}
if (clearBol) {
super.paintComponent(g);
circles.clear();
clearBol= false;
}
Thank you!
Most of the structure of your class needs to be changed
Your MyPanel should have a better name to give its functionality, maybe something like DrawingPanel.
The DrawingPanel is then responsible for managing the Circles to be painted. So typically you would just use an ArrayList to hold the Circle information.
Then you would add a method to the class, like addCircle(...) to add the Circle information to the ArrayList and then invoke repaint().
Then in your paintComponent(...) method the first thing you do is invoke super.paintComponent(...) to clear the panel. Then you iterate through the ArrayList and paint all the Circles. There will be no need for the Boolean values to check the state of the class. The ArrayList will either have circles or it won't.
You would also need a method like clearCircles(). This would simply remove all the Circles from the ArrayList and invoke repaint() on itself.
Your Circle class should NOT extend JPanel. It should just be a class that contains the information need to paint the circle: x/y location, size of circle and color of circle.
Now your frame is responsible of displaying your DrawingPanel and the buttons.
When you click the "Clear" button you simply invoke the clearCircles() method of the DrawingPanel.
For your MouseListener you simply invoke the addCircle(...) method of your DrawingPanel once you have all the information needed to create a Circle instance.
For a complete working example that incorporates all these suggestions check out the DrawOnComponent example found in Custom Painting Approaches
I have 4 classes:
Draw, Rectangle (extends Draw), FreeHand (extends Draw) and a test class.
I add rectangles and lines drawn by free hand to an arrayList.
I have a menubar with choices Back and Clear. Back removes the last drawn object. It is done by removing the last object in the arraylist. Clear clears the windows. It is done by clear the arraylist from all items.
Now to my problem: The window does not clear. I don't know how to write the code to make it repaint properly so that the items removes from the window.
Can you please help me with how the code for this would look like, and where I put it. I appreciate it, thank you.
My problem no 2:
After I have removed the last item in the arraylist I need to draw all the items in the arrayList. I have tried
for (Draw d : shapeList) {
d.draw(g2);
}
But it does not work. Any suggestions?
Class Draw:
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public abstract class Draw extends JPanel {
public int startX, startY, endX, endY, width, height, w, h;
public String color = "Black";
public Draw(int startX, int startY, int width, int height) {
this.startX = startX;
this.startY = startY;
this.width = width;
this.height = height;
}
public abstract void draw(Graphics2D g);
#Override
public int getX() {
return startX;
}
public void setX(int startX) {
this.startX = startX;
}
#Override
public int getY() {
return startY;
}
public void setY(int startY) {
this.startY = startY;
}
#Override
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
#Override
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public void setColor(String color) {
this.color = color;
}
}
Class Rectangle:
import java.awt.Color;
import java.awt.Graphics2D;
public class Rectangle extends Draw {
public Rectangle(int x, int y, int width, int height) {
super(x, y, width, height);
}
#Override
public void draw(Graphics2D g2) {
switch (color) {
case "Red":
g2.setColor(Color.RED);
break;
case "Green":
g2.setColor(Color.GREEN);
break;
case "Blue":
g2.setColor(Color.BLUE);
break;
case "Yellow":
g2.setColor(Color.YELLOW);
break;
case "Orange":
g2.setColor(Color.ORANGE);
break;
case "Black":
g2.setColor(Color.BLACK);
break;
}
g2.drawRect(getX(), getY(), getWidth(), getHeight());
}
}
Class FreeHand:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
public class FreeHand extends Draw {
public FreeHand(int x, int y, int width, int height) {
super(x, y, width, height);
}
/* public FreeHand() {
super();
}*/
#Override
public void draw(Graphics2D g2) {
//Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(3));
switch (color) {
case "Red":
g2.setColor(Color.RED);
break;
case "Green":
g2.setColor(Color.GREEN);
break;
case "Blue":
g2.setColor(Color.BLUE);
break;
case "Yellow":
g2.setColor(Color.YELLOW);
break;
case "Orange":
g2.setColor(Color.ORANGE);
break;
case "Black":
g2.setColor(Color.BLACK);
break;
}
g2.drawLine(getX(), getY(), getWidth(), getHeight());
}
}
Testclass:
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
public class JavaApplication30 extends JFrame implements ActionListener {
public ArrayList<Draw> shapeList = new ArrayList<>();
int startX, startY, endX, endY, w, h;
private JPanel topPanel;
private JPanel bottomPanel;
private JPanel orangePanel;
private JPanel greenPanel;
private JPanel bluePanel;
private JPanel blackPanel;
private JPanel redPanel;
private JPanel yellowPanel;
private JPanel leftPanel;
private JPanel rightPanel;
private JPanel colorPanel;
private JMenuBar menuBar;
private JMenu menu;
private JMenuItem menuItem1;
private JMenuItem menuItem2;
private JMenuItem menuItem3;
private JComboBox comboBox;
private JLabel leftLabel;
private JLabel commaLabel;
private JLabel colorLabel;
private static JLabel xLabel;
private static JLabel yLabel;
private final String labelText = "Coordinates: ";
private final String comma = ",";
private final String color = "Color: ";
private final String[] boxOptions = new String[] {"Rectangle", "Freehand"};
private String pickedColor = "Black";
private String x = "";
private String y = "";
Container cp = getContentPane();
private int count = 0;
public JavaApplication30(String title) {
super(title);
this.setLayout(new BorderLayout());
this.setLocationRelativeTo(null);
this.setSize(840, 500);
this.initComponents();
this.initMenu();
this.setVisible(true);
}
private void initComponents() {
cp.setBackground(Color.WHITE);
comboBox = new JComboBox(boxOptions);
topPanel = new JPanel(new GridLayout(1,7));
bottomPanel = new JPanel(new GridLayout(1,2));
orangePanel = new JPanel();
greenPanel = new JPanel();
bluePanel= new JPanel();
blackPanel = new JPanel();
redPanel = new JPanel();
yellowPanel = new JPanel();
colorPanel = new JPanel();
rightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
leftPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
comboBox.setSelectedIndex(0);
comboBox.addActionListener(this);
topPanel.setPreferredSize(new Dimension(0,40));
bottomPanel.setPreferredSize(new Dimension(0,30));
colorPanel.setPreferredSize(new Dimension(60,20));
leftLabel = new JLabel(labelText);
commaLabel = new JLabel(comma);
colorLabel = new JLabel(color);
bottomPanel.setBackground(Color.LIGHT_GRAY);
orangePanel.setBackground(Color.ORANGE);
greenPanel.setBackground(Color.GREEN);
bluePanel.setBackground(Color.BLUE);
blackPanel.setBackground(Color.BLACK);
redPanel.setBackground(Color.RED);
yellowPanel.setBackground(Color.YELLOW);
colorPanel.setBackground(Color.BLACK);
topPanel.add(orangePanel);
topPanel.add(greenPanel);
topPanel.add(bluePanel);
topPanel.add(blackPanel);
topPanel.add(redPanel);
topPanel.add(yellowPanel);
topPanel.add(comboBox);
rightPanel.add(colorLabel);
rightPanel.add(colorPanel);
bottomPanel.add(leftPanel);
bottomPanel.add(rightPanel);
this.add(topPanel, BorderLayout.PAGE_START);
this.add(bottomPanel, BorderLayout.PAGE_END);
}
#Override
public void paint(Graphics g) {
if(count == 0) {
cp.repaint();
}
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(new BasicStroke(1));
for (Draw d : shapeList) {
d.draw(g2);
}
if (startX != 0 && startY != 0 && endX != 0 && endY != 0) {
int width = Math.abs(startX - endX);
int height = Math.abs(startY - endY);
int minX = Math.min(startX, endX);
int minY = Math.min(startY, endY);
Rectangle r = new Rectangle(minX, minY, width, height);
g2.setPaint(Color.WHITE);
g2.fillRect(r.getX(), r.getY(), r.getWidth(), r.getHeight());
r.setColor(pickedColor);
r.draw(g2);
}
}
#Override
public void actionPerformed(ActionEvent e) {
count++;
if (e.getSource().equals(menuItem1)) {
shapeList.clear();
//Code to clear window
}
if (e.getSource().equals(menuItem2)) {
shapeList.remove(shapeList.size() - 1);
//Code to clear window
Graphics g = getGraphics();
Graphics2D g2 = (Graphics2D) g;
for (Draw d : shapeList) {
d.draw(g2);
}
}
if (e.getSource().equals(menuItem3)) {
//Exit
}
if (e.getSource().equals(comboBox)) {
JComboBox cb = (JComboBox)e.getSource();
if (cb.getSelectedItem().equals("Rectangle")) {
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
startX = e.getX();
startY = e.getY();
endX = startX;
endY = startY;
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
endX = e.getX();
endY = e.getY();
int width = Math.abs(startX - endX);
int height = Math.abs(startY - endY);
int minX = Math.min(startX, endX);
int minY = Math.min(startY, endY);
Rectangle r = new Rectangle(minX, minY, width, height);
shapeList.add(r);
r.setColor(pickedColor);
startX = 0;
startY = 0;
endX = 0;
endY = 0;
repaint();
}
});
this.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
endX = e.getX();
endY = e.getY();
repaint();
}
});
}
else if (cb.getSelectedItem().equals("Freehand")) {
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
startX = e.getX();
startY = e.getY();
}
});
this.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
Graphics g = getGraphics();
Graphics2D g2 = (Graphics2D) g;
FreeHand fh = new FreeHand(startX, startY, e.getX(), e.getY());
shapeList.add(fh);
fh.setColor(pickedColor);
fh.draw(g2);
startX = e.getX();
startY = e.getY();
}
});
}
}
}
private void initMenu() {
menuBar = new JMenuBar();
menu = new JMenu("File");
menuBar.add(menu);
menuItem1 = new JMenuItem("Clear");
menuItem2 = new JMenuItem("Back");
menuItem3 = new JMenuItem("Exit");
menu.add(menuItem1);
menu.add(menuItem2);
menu.addSeparator();
menu.add(menuItem3);
menu.setMnemonic(KeyEvent.VK_A);
KeyStroke ks1 = KeyStroke.getKeyStroke(KeyEvent.VK_N, InputEvent.CTRL_MASK); //Crtl+n
KeyStroke ks2 = KeyStroke.getKeyStroke(KeyEvent.VK_I, InputEvent.CTRL_MASK); //Ctrl+i
KeyStroke ks3 = KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_MASK); //Ctrl+e
menuItem1.setAccelerator(ks1);
menuItem2.setAccelerator(ks2);
menuItem3.setAccelerator(ks3);
menuItem1.addActionListener(this);
menuItem2.addActionListener(this);
menuItem3.addActionListener(this);
setJMenuBar(menuBar);
}
public static void main(String args[]) {
new JavaApplication30("Draw");
}
}
Your problem looks to be that your paint method is not calling the super's paint method since this will have the component clean itself of all "dirty" image bits. But having said that, you shouldn't draw directly in the JFrame. Instead draw in the paintComponent method of a JComponent or JPanel, and in that method be sure to call the super's paintComponent method:
public class MyDrawingPanel extends JPanel {
#Override
proteced void paintComponent(Graphics g) {
super.paintComponent(g); // don't forget this!
// do your drawing here
}
}
Also, why does your Draw class, and thus all classes that derive from it, extend JPanel when it is not being used as a JPanel? You are giving a lot of unnecessary overhead to these classes this way.
Edit
You ask:
So you mean I should move Everything in the paint-method to the paintComponent-method in Draw? Why protected?
No. I mean that Draw should not extend JPanel, but instead should be a logical class, not a Swing component-derived class. I think that you should create a new class, say called MyDrawingPanel where you do all of your drawing. Please see my code snippet above. Also paintComponent is declared in JComponent to be protected, not public, and I see no advantage to making it public when overriding it, so I recommend leaving it protected.
Please read the Swing Info Links to see the Swing graphics tutorials and give them a read.
Edit 2
You're also using a getGraphics() call on a component to get your Graphics object, not good as this will return an unstable Graphics object. Instead do all drawing in the paintComponent method or on a BufferedImage (that again is drawn in paintComponent).
Edit 3
Some of my code examples:
Minimal way to make a cleanable drawing area
Changing JPanel Graphics g color drawing line
Graphics in repaint draws random lines
I have some issues with my paint program in Java.
I have a JComboBox where I can choose to draw either a rectangle or by freehand. The objects are added to an ArrayList. I want to be able to switch between drawing a rectangle and by free hand, and then back to drawing a rectangle, and then by free hand... and so on.
If I do that as the code looks like now, it first draws rectangles fine and then when I switch to free hand it draws lines fine, but then when I switch back to rectangles it still draws lines (or sometimes lines together with weird looking rectangles). The more I switch the weirder it gets.
Can anyone see what is wrong with the code, because I can't?
public abstract class Draw {
public int startX, startY, endX, endY, width, height, w, h;
public String color = "Black";
public Draw(int startX, int startY, int width, int height) {
this.startX = startX;
this.startY = startY;
this.width = width;
this.height = height;
}
public abstract void draw(Graphics2D g);
public int getX() {
return startX;
}
public void setX(int startX) {
this.startX = startX;
}
public int getY() {
return startY;
}
public void setY(int startY) {
this.startY = startY;
}
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 void setColor(String color) {
this.color = color;
}
}
public class Rectangle extends Draw {
public Rectangle(int x, int y, int width, int height) {
super(x, y, width, height);
}
#Override
public void draw(Graphics2D g2) {
g2.drawRect(getX(), getY(), getWidth(), getHeight());
}
}
public class FreeHand extends Draw {
public FreeHand(int x, int y, int width, int height) {
super(x, y, width, height);
}
#Override
public void draw(Graphics2D g2) {
g2.drawLine(getX(), getY(), getWidth(), getHeight());
}
}
public class PaintProgram extends JFrame implements ActionListener {
public ArrayList<Draw> shapeList = new ArrayList<>();
int startX, startY, endX, endY, w, h;
private JPanel topPanel;
private JPanel bottomPanel;
private JPanel leftPanel;
private JPanel rightPanel;
private JComboBox comboBox;
private final String[] boxOptions = new String[] {"Rectangle", "Freehand"};
Container cp = getContentPane();
private int count = 0;
public JavaApplication30(String title) {
super(title);
this.setLayout(new BorderLayout());
this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
this.setLocationRelativeTo(null);
this.setSize(840, 500);
this.initComponents();
this.setVisible(true);
}
private void initComponents() {
cp.setBackground(Color.WHITE);
comboBox = new JComboBox(boxOptions);
topPanel = new JPanel();
bottomPanel = new JPanel(new GridLayout(1,2));
rightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
leftPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
comboBox.setSelectedIndex(0);
comboBox.addActionListener(this);
topPanel.setPreferredSize(new Dimension(0,40));
bottomPanel.setPreferredSize(new Dimension(0,30));
bottomPanel.setBackground(Color.LIGHT_GRAY);
topPanel.add(comboBox);
bottomPanel.add(leftPanel);
bottomPanel.add(rightPanel);
this.add(topPanel, BorderLayout.PAGE_START);
this.add(bottomPanel, BorderLayout.PAGE_END);
}
#Override
public void paint(Graphics g) {
if(count == 0) {
cp.repaint();
}
Graphics2D g2 = (Graphics2D) g;
for (Draw d : shapeList) {
d.draw(g2);
}
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(new BasicStroke(1));
if (startX != 0 && startY != 0 && endX != 0 && endY != 0) {
int width = Math.abs(startX - endX);
int height = Math.abs(startY - endY);
int minX = Math.min(startX, endX);
int minY = Math.min(startY, endY);
Rectangle r = new Rectangle(minX, minY, width, height);
g2.setPaint(Color.WHITE);
g2.fillRect(r.getX(), r.getY(), r.getWidth(), r.getHeight());
r.setColor(pickedColor);
r.draw(g2);
}
}
#Override
public void actionPerformed(ActionEvent e) {
count++;
if (e.getSource().equals(comboBox)) {
JComboBox cb = (JComboBox)e.getSource();
if (cb.getSelectedItem().equals("Rectangle")) {
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
startX = e.getX();
startY = e.getY();
endX = startX;
endY = startY;
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
endX = e.getX();
endY = e.getY();
int width = Math.abs(startX - endX);
int height = Math.abs(startY - endY);
int minX = Math.min(startX, endX);
int minY = Math.min(startY, endY);
Rectangle r = new Rectangle(minX, minY, width, height);
shapeList.add(r);
r.setColor(pickedColor);
startX = 0;
startY = 0;
endX = 0;
endY = 0;
repaint();
}
});
this.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
endX = e.getX();
endY = e.getY();
repaint();
}
});
}
else if (cb.getSelectedItem().equals("Freehand")) {
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
startX = e.getX();
startY = e.getY();
addCoordinate(startX, startY);
}
});
this.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
Graphics g = getGraphics();
Graphics2D g2 = (Graphics2D) g;
FreeHand fh = new FreeHand(startX, startY, e.getX(), e.getY());
shapeList.add(fh);
fh.setColor(pickedColor);
fh.draw(g2);
startX = e.getX();
startY = e.getY();
}
});
}
}
}
public static void main(String args[]) {
new PaintProgram("Paint");
}
}
You add MouseListeners but you do not remove them. Every time you choose something in the combobox, a new listener is added. So when you draw something every listener is applied and weird stuff will happen.
You should remove the previous MouseListener before adding a new one. You might have to remember it in an instance variable.
Alternatively, you can add all listeners at the start, but check the value of the combobox inside the listener. If the value does not correspond to what the listener is for, it should do nothing.
EDIT: Here is how you can remove all listeners
for (MouseListener listener : this.getMouseListeners()) {
this.removeMouseListener(listener);
}
for (MouseMotionListener listener : this.getMouseMotionListeners()) {
this.removeMouseMotionListener(listener);
}
Put this code in before you add the new listeners in the actionPerformed() method
As was stated here and here previously, do not add MouseListeners within your ActionListener, instead, create a single MosueListener and determine what you want to do based on the currently selected item.
Basically, you keep adding a new MouseListener each time actionPerformed is called...they are accumulating...
A solution would be to use a single MouseListener and a factory of some kind...
Start by defining the factory interface...
public interface DrawFactory {
public Draw createDrawing(int x, int y, int width, int height, Color color);
public void addPoint(Draw draw, int x, int y);
}
Create a implementation of the factory for each type of shape you want to draw...
public class RectangleFactory implements DrawFactory {
#Override
public Draw createDrawing(int x, int y, int width, int height, Color color) {
return new Rectangle(x, y, width, height);
}
#Override
public void addPoint(Draw draw, int x, int y) {
// Does nothing...
}
#Override
public boolean isMutable() {
return false;
}
#Override
public String getName() {
return "Rectangle";
}
#Override
public String toString() {
return getName();
}
}
public class FreeHandFactory implements DrawFactory {
#Override
public Draw createDrawing(int x, int y, int width, int height, Color color) {
return new FreeHand(x, y, width, height);
}
#Override
public void addPoint(Draw draw, int x, int y) {
if (draw instanceof FreeHand) {
FreeHand fh = (FreeHand)draw;
//fh.addPoint(x, y);
}
}
#Override
public boolean isMutable() {
return true;
}
#Override
public String getName() {
return "Free Hand";
}
#Override
public String toString() {
return getName();
}
}
Next, create a custom component that extends from JPanel which will act as the primary drawing surface, this will be repsonsible for monitoring the MouseLstener and painting the Draw instances, as was mentioned here
public class DrawSurface extends JPanel {
private DrawFactory factory;
private Draw currentDraw;
private List<Draw> shapeList = new ArrayList<>();
private Color drawColor;
public DrawSurface() {
shapeList = new ArrayList<>(25);
MouseAdapter ma = new MouseAdapter() {
private Point pressPoint;
#Override
public void mousePressed(MouseEvent e) {
pressPoint = e.getPoint();
}
#Override
public void mouseReleased(MouseEvent e) {
DrawFactory factory = getDrawFactory();
if (factory != null) {
Point p = e.getPoint();
if (factory.isMutable() && currentDraw != null) {
factory.addPoint(currentDraw, p.x, p.y);
} else {
int x = Math.min(p.x, pressPoint.x);
int y = Math.min(p.y, pressPoint.y);
int width = Math.abs(p.x - pressPoint.x);
int height = Math.abs(p.y - pressPoint.y);
Draw draw = factory.createDrawing(x, y, width, height, getDrawColor());
shapeList.add(draw);
if (factory.isMutable()) {
currentDraw = draw;
}
}
}
}
};
}
public DrawFactory getDrawFactory() {
return factory;
}
public void setDrawFactory(DrawFactory factory) {
this.factory = factory;
currentDraw = null;
}
public Color getDrawColor() {
return drawColor;
}
public void setDrawColor(Color drawColor) {
this.drawColor = drawColor;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Draw draw : shapeList) {
draw.draw(g2d);
}
g2d.dispose();
}
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
Next, change your boxOptions from String to DrawFactory, this will make it easier to determine which factory you should use. Don't forget to add a reference to the DrawSurface
private final DrawFactory[] boxOptions = new DrawFactory[]{new RectangleFactory(), new FreeHandFactory()};
private DrawSurface drawSurface;
In your initComponents create a new instance of DrawSurface and add it to your frame...
private void initComponents() {
//...
drawSurface = new DrawSurface();
this.add(drawSurface);
}
Change your actionPerformed method to look more like...
#Override
public void actionPerformed(ActionEvent e) {
count++;
drawSurface.setDrawFactory((DrawFactory)comboBox.getSelectedItem());
}
Not sure how you are determining the current color as you example code is incomplete, but basically, you want to set the drawColor of the DrawSurface similarly.
Get rid of the paint method in the PaintProgram as you shouldn't be overriding the paint method of top level containers, which you've been advised against at least once, if not twice.
The point of all this is simple, when you want to add a new "drawing shape", you create a Draw and DrawFactory for it and add the factory to the combo box ... work done...
I created 2 car objects using my CarPanel. Then I used to keylistener to repaint the object so it will look like moving forward.
But the problem is the whole panel moves along, even though I just increased the distance to the 'x' coordinate.
If I were to resize the window, it sometimes doesn't even clear the component.
public class RacingCars extends JFrame {
CarPanel car1;
CarPanel car2;
//Constructor
public RacingCars(){
setLayout(new GridLayout(2,1));
car1 = new CarPanel('w',Color.RED);
car2 = new CarPanel('k',Color.blue);
car1.setBackground(Color.black);
this.add(car1, BorderLayout.NORTH);
this.add(car2, BorderLayout.SOUTH);
this.addKeyListener(new keyListener());
}
class keyListener extends KeyAdapter{
public void keyPressed(KeyEvent e){
if(e.getKeyChar()=='w'){
car1.moveCar();
}
if(e.getKeyChar()=='k'){
car2.moveCar();
}
}
}
}
CarPanel::
public class CarPanel extends JPanel {
private char forwardKey = 'w';
private boolean reachedTarget = false;
private Color color = Color.blue;
private int x= 10;
private int y= 10;
private int panelWidth;
private int panelHeight;
//default Constructor
public CarPanel(){
}
//overloaded Constructor
public CarPanel(char key, Color color){
this.forwardKey=key;
this.color = color;
}
protected void paintComponent(Graphics g){
super.paintComponent(g);
panelWidth= getWidth();
panelHeight= getHeight();
//draw a Car
g.setColor(color);
//polygon points
int t_x[]= {x+10,x+20,x+30,x+40};
int t_y[]= {y+10,y,y,y+10};
g.fillPolygon(t_x,t_y,t_x.length);
g.fillRect(x, y+10, 50, 10);
g.fillArc(x+10, y+20, 10, 10, 0, 360);
g.fillArc(x+30, y+20, 10, 10, 0, 360);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(750,100);
}
public void moveCar(){
if(this.x < panelWidth){
this.x+=10;
repaint();
}
}
}
Solution
Because of this code, I always had that extra space. but no idea how it supposed to cause to this strange behavior.
/*
public int getX(){
return this.x;
}
*/
But the problem is the whole panel moves along, ..
No it doesn't. The 'green dot' proves that.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class RacingCars extends JFrame {
CarPanel car1;
CarPanel car2;
//Constructor
public RacingCars(){
setLayout(new GridLayout(2,1));
car1 = new CarPanel('w',Color.RED);
car2 = new CarPanel('k',Color.blue);
car1.setBackground(Color.black);
this.add(car1, BorderLayout.NORTH);
this.add(car2, BorderLayout.SOUTH);
this.addKeyListener(new MyKeyListener());
}
class MyKeyListener extends KeyAdapter{
public void keyPressed(KeyEvent e){
if(e.getKeyChar()=='w'){
car1.moveCar();
}
if(e.getKeyChar()=='k'){
car2.moveCar();
}
}
}
public static void main(String[] args) {
//Create the frame on the event dispatching thread
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
RacingCars rc = new RacingCars();
rc.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
rc.pack();
rc.setVisible(true);
}
});
}
}
class CarPanel extends JPanel {
private char forwardKey = 'w';
private boolean reachedTarget = false;
private Color color = Color.blue;
private int x= 10;
private int y= 10;
private int panelWidth;
private int panelHeight;
//default Constructor
public CarPanel(){
}
//overloaded Constructor
public CarPanel(char key, Color color){
this.forwardKey=key;
this.color = color;
}
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillOval(0,0,25,25);
panelWidth= getWidth();
panelHeight= getHeight();
//draw a Car
g.setColor(color);
//polygon points
int t_x[]= {x+10,x+20,x+30,x+40};
int t_y[]= {y+10,y,y,y+10};
g.fillPolygon(t_x,t_y,t_x.length);
g.fillRect(x, y+10, 50, 10);
g.fillArc(x+10, y+20, 10, 10, 0, 360);
g.fillArc(x+30, y+20, 10, 10, 0, 360);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(750,100);
}
public void moveCar(){
if(this.x < panelWidth){
this.x+=10;
repaint();
}
}
}