Strange behavior when I move a JComponent in a JPanel - java

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()

Related

Trying to create Circles using MouseListener and MouseMotionListener - what I am doing wrong?

I'm trying to create Circles in JFrame using JComponent. Here's what I'm trying to achieve:
And here's what's happening with my code:
I have no idea what's causing this problem. Here's my code:
CircleViewer.java
import javax.swing.JFrame;
import java.awt.event.*;
public class CircleViewer
{
public static void main(String[] args)
{
final CirclePanel panel = new CirclePanel();
class MousePressListener implements MouseListener, MouseMotionListener
{
public void mouseClicked(MouseEvent event) { }
public void mouseEntered(MouseEvent event) { }
public void mouseExited(MouseEvent event) { }
public void mouseWheelMoved(MouseWheelEvent event) { }
public void mouseMoved(MouseEvent event) { }
public void mousePressed(MouseEvent event)
{
var x = event.getX();
var y = event.getY();
panel.addCircle(x, y);
}
public void mouseDragged(MouseEvent event)
{
var x = event.getX();
var y = event.getY();
panel.moveTo(x, y);
}
public void mouseReleased(MouseEvent event)
{
panel.finalMove();
}
}
MousePressListener listener = new MousePressListener();
panel.addMouseListener(listener);
panel.addMouseMotionListener(listener);
JFrame frame = new JFrame("Circle Shapes");
frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.setVisible(true);
}
private static final int FRAME_WIDTH = 700;
private static final int FRAME_HEIGHT = 500;
}
CirclePanel.java
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JComponent;
import java.util.ArrayList;
import java.awt.Color;
import java.lang.Math;
import java.awt.BasicStroke;
import java.awt.Stroke;
public class CirclePanel extends JComponent
{
private int lineX;
private int lineY;
private boolean isDraged;
private ArrayList<Circle> circleList;
private BasicStroke dashLine;
public CirclePanel()
{
this.circleList = new ArrayList<Circle>();
this.isDraged = false;
this.lineX = 0;
this.lineY = 0;
this.dashLine = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[]{6}, 0);
}
public void addCircle(int x, int y)
{
lineX = x;
lineY = y;
isDraged = true;
circleList.add(new Circle(x, y, 0, Color.RED));
repaint();
}
public void moveTo(int x, int y)
{
var circleTemp = circleList.get(circleList.size() - 1);
isDraged = true;
var tempR = (int)Math.sqrt(Math.pow(x - circleTemp.get(0), 2) + Math.pow(y - circleTemp.get(1), 2));
System.out.println(tempR);
var tempX = circleTemp.get(0) - (tempR / 2);
var tempY = circleTemp.get(1) - (tempR / 2);
circleList.get(circleList.size() - 1).setCords(tempX, tempY, tempR);
lineX = x;
lineY = y;
repaint();
}
public void finalMove()
{
isDraged = false;
circleList.get(circleList.size() - 1).setColor(Color.BLUE);
repaint();
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
Stroke defaultStroke;
defaultStroke = g2.getStroke();
if (!circleList.isEmpty())
{
if (isDraged)
{
g2.setColor(Color.RED);
g2.setStroke(dashLine);
g2.drawLine(circleList.get(circleList.size() - 1).get(0), circleList.get(circleList.size() - 1).get(1), lineX, lineY);
}
for (Circle circle : circleList)
{
g2.setStroke(defaultStroke);
circle.draw(g2);
isDraged = false; //this is prob reduntant
}
}
}
}
And finally, Circle.java
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
public class Circle
{
private int x;
private int y;
private int radius;
private Color color;
public Circle(int x, int y, int radius, Color color)
{
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
}
public int get(int option)
{
switch (option)
{
case 0:
return this.x;
case 1:
return this.y;
case 2:
return this.radius;
}
return 0;
}
public Color get()
{
return this.color;
}
public void setCords(int x, int y, int r)
{
this.x = x;
this.y = y;
this.radius = r;
}
public void set(int option, int value)
{
switch (option)
{
case 0: //set x
this.x = value;
break;
case 1:
this.y = value;
break;
case 2:
radius = value;
break;
}
}
public void setColor(Color color)
{
this.color = color;
}
public void draw(Graphics2D g2)
{
g2.setColor(color);
g2.draw(new Ellipse2D.Double(x, y, radius, radius));
}
}
Rather than finding the specific problem in you code, I just decided to make a cleaner implementation. Using this circle class
class Circle {
final int x;
final int y;
int radius;
public Circle(int x, int y, int radius) {
this.x = x;
this.y = y;
this.radius = radius;
}
}
You can implement CirclePanel like this:
public class CirclePanel extends JPanel {
private static final Stroke DASHED = new BasicStroke(1,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_BEVEL,
0, new float[]{6}, 0);
public Color oldCircleColor = Color.BLUE;
public Color newCircleColor = Color.RED;
public Color backgroundColor = Color.LIGHT_GRAY;
private final List<Circle> oldCircles = new ArrayList<>();
private Circle newCircle = null;
private int mouseX = 0;
private int mouseY = 0;
private class MouseHelper implements MouseListener, MouseMotionListener {
#Override public void mouseMoved(MouseEvent e) {}
#Override public void mouseClicked(MouseEvent e) {}
#Override public void mousePressed(MouseEvent e) {}
#Override public void mouseEntered(MouseEvent e) {}
#Override public void mouseExited(MouseEvent e) {}
#Override
public void mouseDragged(MouseEvent e) {
mouseX = e.getX();
mouseY = e.getY();
if (newCircle == null) {
newCircle = new Circle(mouseX, mouseY, 0);
} else {
int dX = newCircle.x - mouseX;
int dY = newCircle.y - mouseY;
newCircle.radius = (int) Math.sqrt(dX*dX + dY*dY);
}
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
if (newCircle != null) {
oldCircles.add(newCircle);
newCircle = null;
repaint();
}
}
}
public CirclePanel() {
MouseHelper helper = new MouseHelper();
addMouseListener(helper);
addMouseMotionListener(helper);
setPreferredSize(new Dimension(400, 400));
}
#Override
public void paint(Graphics g) {
g.setColor(backgroundColor);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(oldCircleColor);
for (Circle c : oldCircles) {
drawCircle(g, c);
}
Circle c = newCircle;
if (c != null) {
g.setColor(newCircleColor);
drawCircle(g, c);
Graphics2D g2 = (Graphics2D) g.create();
g2.setStroke(DASHED);
g2.drawLine(c.x, c.y, mouseX, mouseY);
g2.dispose();
}
}
private void drawCircle(Graphics g, Circle c) {
// note: drawOval takes top-left corner and diameter, NOT center and radius
g.drawOval(c.x - c.radius, c.y - c.radius, c.radius * 2, c.radius * 2);
}
}
And test to see that it works
public static void main(String[] args) {
JFrame frame = new JFrame();
CirclePanel panel = new CirclePanel();
frame.add(panel);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
Like #SirLenz0rlot mentioned in the comments there is a bug in your moveTo method. The problem is the call circleList.get(circleList.size() - 1).setCords(tempX, tempY, tempR); which is setting the position of the circle to tempX, tempY, but you probably only want to set the radius of the circle, while the coords are not changed.
Changing the moveTo method to the code below should help:
public void moveTo(int x, int y)
{
var circleTemp = circleList.get(circleList.size() - 1);
isDraged = true;
var tempR = (int)Math.sqrt(Math.pow(x - circleTemp.get(0), 2) + Math.pow(y - circleTemp.get(1), 2));
System.out.println(tempR);
var tempX = circleTemp.get(0) - (tempR / 2);
var tempY = circleTemp.get(1) - (tempR / 2);
//EDITED HERE
circleList.get(circleList.size() - 1).setCords(circleTemp.get(0), circleTemp.get(1), tempR);//using getX() and getY() on your circle would be easier to understand here, but this should also work...
lineX = x;
lineY = y;
repaint();
}
For some reason your circleTemp.get(0) and (1) doesn't give you the (x,y) coordinates
circleTemp.get(0)
What you should do is save the circle's (x,y) coordinates, and use them in the moveTo method.
Correct Change
And in the end for the draw line, you should again, use the saved (x,y) coordinates and the new coordinated of the mouse. (not the circleList.size() -1).get(0) - Line 67)
(Ex. I added the drawLineX and drawLineY)

How to paint an arrayList of shapes Java

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();
}
}
}

JPanel not repainting via Timer

So I have this class, which is my main panel:
public class Frogger extends JPanel {
private static final int WINDOW_WIDTH = 640;
private static final int WINDOW_HEIGHT = 480;
private static final Entity frog;
private static final Entity car;
static {
frog = new Entity("sprites/frog.png", WINDOW_WIDTH / 2 - 75, WINDOW_HEIGHT - 190);
car = new Entity("sprites/car.png", 0, WINDOW_HEIGHT / 2);
}
private static KeyListener genGameListener() {
return new KeyListener() {
#Override
public void keyPressed(KeyEvent e) {}
#Override
public void keyReleased(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT:
frog.move(-50, 0);
System.out.println("lol");
break;
}
}
#Override
public void keyTyped(KeyEvent e) {}
};
}
#Override
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.WHITE);
frog.draw(g2d);
car.draw(g2d);
}
public static void main(String[] args) {
JFrame frame = new JFrame("Frogger!");
Frogger frogger = new Frogger();
frogger.addKeyListener(genGameListener());
frogger.setFocusable(true);
frame.add(frogger);
frame.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Timer timer = new Timer(100, (ActionEvent e) -> {
car.move(50, WIDTH);
frogger.repaint();
});
}
}
With the Entity class as such:
public class Entity {
private BufferedImage sprite;
private final AffineTransform at;
private final int width;
private final int height;
private int x;
private int y;
public Entity(String imageFileName, int x, int y) {
sprite = null;
try {
this.sprite = ImageIO.read(new File(imageFileName));
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
width = sprite.getWidth();
height = sprite.getHeight();
this.x = x;
this.y = y;
at = new AffineTransform();
at.translate(x, y);
}
public void draw(Graphics2D g) {
g.drawImage(sprite, at, null);
}
public void rotate(int radians) {
at.rotate(radians);
}
public void move(int dx, int dy) {
x += dx;
y += dy;
at.translate(dx, dy);
}
...
However, my key actions are not updating the sprites, and the car is not moving across the screen like the Timer is scheduled to do. Why isn't the frame being repainted?
You never call start() on your Timer object.

Switch between drawing a rectangle and by freehand

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...

Java Racing game: JPanel component moves along when I repaint the car object

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();
}
}
}

Categories

Resources