Multiple Rectangle Generation [duplicate] - java

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Drawing multiple pixels/rectangles
In my code i wrote a method that creates a rectangle at mouseX, mouseY. but all it does is update the position of that rectangle so it follows the mouse, i want it to create a new one at the mouse every time the method runs, can someone please help?
this is my method
public void drawParticle(float x, float y){
g.drawRect(x, y, 4, 4);
}
The main class Control call the drawParticle method;
import java.awt.Point;
import java.awt.geom.Point2D;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.state.BasicGameState;
import org.newdawn.slick.state.StateBasedGame;
public class Control extends BasicGameState {
public static final int ID = 1;
public Methods m = new Methods();
public Graphics g = new Graphics();
int mouseX;
int mouseY;
public void init(GameContainer container, StateBasedGame game) throws SlickException{
}
public void render(GameContainer container, StateBasedGame game, Graphics g) throws SlickException {
m.drawParticle(mouseX, mouseY);
}
public void update(GameContainer container, StateBasedGame game, int delta) {
}
public void mousePressed(int button, int x, int y) {
mouseX = x;
mouseY = y;
}
public int getID() {
return ID;
}
}
Thanks - Shamus

The long and short of it is, you need to maintain a list of the objects you want to paint on each paint cycle.
public class ColorMeRectangles {
public static void main(String[] args) {
new ColorMeRectangles();
}
public ColorMeRectangles() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setLayout(new BorderLayout());
frame.add(new RectanglePane());
frame.setVisible(true);
}
});
}
public class RectanglePane extends JPanel {
private Point mousePoint;
private List<Partical> particals;
private Timer generator;
private int min = -4;
private int max = 4;
public RectanglePane() {
setBackground(Color.BLACK);
particals = new ArrayList<Partical>(25);
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
mousePoint = e.getPoint();
repaint();
}
});
generator = new Timer(125, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (mousePoint != null) {
int x = mousePoint.x + (min + (int) (Math.random() * ((max - min) + 1)));
int y = mousePoint.y + (min + (int) (Math.random() * ((max - min) + 1)));
Color color = new Color(
(int) (Math.random() * 255),
(int) (Math.random() * 255),
(int) (Math.random() * 255));
particals.add(new Partical(new Point(x, y), color));
repaint();
}
}
});
generator.setRepeats(true);
generator.setCoalesce(true);
generator.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Partical partical : particals) {
partical.paint(g2d);
}
if (mousePoint != null) {
g2d.setColor(Color.WHITE);
g2d.drawRect(mousePoint.x - 2, mousePoint.y - 2, 4, 4);
}
g2d.dispose();
}
}
public class Partical {
private Point location;
private Color color;
public Partical(Point location, Color color) {
this.location = location;
this.color = color;
}
public Point getLocation() {
return location;
}
public Color getColor() {
return color;
}
public void paint(Graphics2D g2d) {
g2d.setColor(color);
g2d.drawRect(location.x - 4, location.y - 4, 8, 8);
}
}
}

One way to do this is to create a List as a member variable and add a new Rectangle each time the user clicks the mouse. Then in your render() method, iterate through the List of Rectangles and paint each one.

Related

bullets creation in a simple game

I am creating a simple game where shapes fall and the player shoots them, but I am having problems creating bullet at every click of the mouse. I have tried various logic with no help, so am just going to put the code up here so you guys can take a look at it and help me out.
The bullet I created is not been created on every click just one is created and it moves on every click which is wrong........I want one bullet to be created per click.
// My main class: mousework2
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.awt.geom.*;
public class mousework2 extends JFrame
{
public static int Width = 300;
public static int Height = 400;
private JPanel p1;
private Image pixMage,gunMage;
public mousework2()
{
super("shoot-em-up");
this.setSize(Width, Height);
this.setResizable(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Dimension pos = Toolkit.getDefaultToolkit().getScreenSize();
int x = (pos.width - Width) / 2;
int y = (pos.height - Height) / 2;
this.setLocation(x, y);
p1 = new CreateImage();
this.add(p1);
this.getContentPane();
Thread t = new recMove(this);
t.start();
}
class recMove extends Thread
{
JFrame b;
public recMove(JFrame b)
{
this.b = b;
}
public void run()
{
while (true) {
b.repaint();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
}
}
}
class CreateImage extends JPanel implements MouseListener
{
ArrayList<fallShape> rect = new ArrayList<fallShape>();
int x_pos = mousework.Width / 2;
int y_pos = mousework.Height - 50;
int bx_pos = mousework.Width / 2;
int by_pos = mousework.Height;
int y_speed = -10;
boolean clicked;
public CreateImage()
{
for (int i = 0; i < 10; i++) {
rect.add(new fallShape(15, 15, rect));
}
Toolkit picx = Toolkit.getDefaultToolkit();
gunMage = picx.getImage("gunner.jpg");
gunMage = gunMage.getScaledInstance(200, -1, Image.SCALE_SMOOTH);
Toolkit pic = Toolkit.getDefaultToolkit();
pixMage = pic.getImage("ballfall3.jpg");
pixMage = pixMage.getScaledInstance(200, -1, Image.SCALE_SMOOTH);
addMouseListener(this);
addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseMoved(MouseEvent e)
{
x_pos = e.getX() - 5;
}
});
}
public void mousePressed(MouseEvent e)
{
if (e.getButton() == 1) {
clicked = true;
}
}
public void mouseReleased(MouseEvent e)
{
if (e.getButton() == 1) {
clicked = false;
}
}
public void mouseExited(MouseEvent e)
{
}
public void mouseEntered(MouseEvent e)
{
}
public void mouseClicked(MouseEvent e)
{
}
public void paint(Graphics g)
{
super.paint(g);
g.drawImage(pixMage, 0, 0, Width, Height, null);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.drawImage(gunMage,x_pos,y_pos,10,20,null);
if (clicked) {
by_pos += y_speed;
Shape bullet = new Rectangle2D.Float(bx_pos, by_pos, 3, 10);
g2.setColor(Color.BLACK);
g2.fill(bullet);
g2.draw(bullet);
}
g2.setColor(Color.RED);
for (fallShape b : rect) {
b.move();
g2.fill(b);
}
}
}
public static void main(String[] args)
{
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run()
{
new mousework2().setVisible(true);
}
});
}
}
And:
// My falling shapes class: fallShape
import java.awt.geom.*;
import java.util.*;
public class fallShape extends Rectangle2D.Float
{
public int x_speed, y_speed;
public int l, b;
public int height = mousework.Height;
public int width = mousework.Width;
public ArrayList<fallShape> fall;
public fallShape(int breadth, int length, ArrayList<fallShape> fall)
{
super((int) (Math.random() * (mousework.Width - 20) + 1), 0, breadth, length);
this.b = breadth;
this.l = length;
this.x_speed = (int) Math.random() * (10) + 1;
this.y_speed = (int) Math.random() * (10) + 1;
this.fall = fall;
}
public void move()
{
Rectangle2D rec = new Rectangle2D.Float(super.x, super.y, b, l);
for (fallShape f : fall) {
if (f != this && f.intersects(rec)) {
int rxspeed = x_speed;
int ryspeed = y_speed;
x_speed = f.x_speed;
y_speed = f.y_speed;
f.x_speed = rxspeed;
f.y_speed = ryspeed;
}
}
if (super.x < 0) {
super.x =+ super.x;
//super.y =+ super.y;
x_speed = Math.abs(x_speed);
}
if (super.x> mousework.Width - 30) {
super.x =+ super.x;
super.y =+ super.y;
x_speed =- Math.abs(x_speed);
}
if (super.y < 0) {
super.y = 0;
y_speed = Math.abs(y_speed);
}
super.x += x_speed;
super.y += y_speed;
}
}
if(clicked){
by_pos+=y_speed;
This code only draws the bullet when the mouse is down. This is because you are setting clicked to false in your mouseReleased method:
public void mouseReleased(MouseEvent e){
if(e.getButton()==1)
clicked=false;
}
If you were to remove the body of the mouseReleased method, your bullet would move properly.
However, say you wanted to have more than just one bullet. Currently, your paint method only draws one bullet at a time. To draw multiple bullets, you would need to create a list of the coordinates of the bullets, and add a new coordinate pair to the list whenever you click. Then, in the paint method, just update each position in a for loop.
ArrayList<Integer> by_poss = new ArrayList<>();
by_poss is the list of all the y-positions of your bullets.
public void mousePressed(MouseEvent e){
if(e.getButton() == 1)
by_poss.add(mousework.Height);
}
The mousePressed method adds a new "bullet", in the form of a y-position, to the coordinates.
public void mouseReleased(MouseEvent e){
//do nothing
}
Nothing needs to happen in the mouseReleased method.
//update the bullets
public void paint(Graphics g){
...
g2.setColor(Color.BLACK);
Shape bullet;
for(int i = 0; i < by_poss.size(); i++){
by_poss.set(i, by_poss.get(i) + y_speed); //move the bullet
bullet = new Rectangle2D.Float(bx_pos, by_poss.get(i), 3, 10);
g2.fill(bullet);
g2.draw(bullet);
}
...
}
The for loop in your paint method draws all the bullets, one by one, usin g the y-positions from the by_poss list.

How to animate Rectangle in JPanel?

I want to learn some tricks about JAVA for my project.
I want to animate my Rectangle leftoright and righttoleft but I can't apply the same functions for ball animation.
In addition,how can I start my ball in different x-direction with a border of y-coordinate ?
Thanks a lot for your advices and helping.
My codes:
import javax.swing.Timer;
import java.util.ArrayList;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class MultipleBall extends JApplet {
public MultipleBall() {
add(new BallControl());
}
class BallControl extends JPanel {
private BallPanel ballPanel = new BallPanel();
private JButton Suspend = new JButton("Suspend");
private JButton Resume = new JButton("Resume");
private JButton Add = new JButton("+1");
private JButton Subtract = new JButton("-1");
private JScrollBar Delay = new JScrollBar();
public BallControl() {
// Group buttons in a panel
JPanel panel = new JPanel();
panel.add(Suspend);
panel.add(Resume);
panel.add(Add);
panel.add(Subtract);
// Add ball and buttons to the panel
ballPanel.setBorder(new javax.swing.border.LineBorder(Color.red));
Delay.setOrientation(JScrollBar.HORIZONTAL);
ballPanel.setDelay(Delay.getMaximum());
setLayout(new BorderLayout());
add(Delay, BorderLayout.NORTH);
add(ballPanel, BorderLayout.CENTER);
add(panel, BorderLayout.SOUTH);
// Register listeners
Suspend.addActionListener(new Listener());
Resume.addActionListener(new Listener());
Add.addActionListener(new Listener());
Subtract.addActionListener(new Listener());
Delay.addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e) {
ballPanel.setDelay(Delay.getMaximum() - e.getValue());
}
});
}
class Listener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == Suspend)
ballPanel.suspend();
else if (e.getSource() == Resume)
ballPanel.resume();
else if (e.getSource() == Add)
ballPanel.add();
else if (e.getSource() == Subtract)
ballPanel.subtract();
}
}
}
class BallPanel extends JPanel {
private int delay = 30;
private ArrayList<Ball> list = new ArrayList<Ball>();
// Create a timer with the initial delay
protected Timer timer = new Timer(delay, new ActionListener() {
/** Handle the action event */
public void actionPerformed(ActionEvent e) {
repaint();
}
});
public BallPanel() {
timer.start();
}
public void add() {
list.add(new Ball());
}
public void subtract() {
if (list.size() > 0)
list.remove(list.size() - 1); // Remove the last ball
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(185, 279, 50, 15);
g.setColor(Color.RED);
g.fillRect(185, 279, 50, 15);
for (int i = 0; i < list.size(); i++) {
Ball ball = (Ball) list.get(i); // Get a ball
g.setColor(ball.color); // Set ball color
// Check boundaries
if (ball.x < 0 || ball.x > getWidth())
ball.dx = -ball.dx;
if (ball.y < 0 || ball.y > getHeight())
ball.dy = -ball.dy;
// Adjust ball position
ball.x += ball.dx;
// ball.y += ball.dy;
g.fillOval(ball.x - ball.radius, ball.y - ball.radius,
ball.radius * 2, ball.radius * 2);
}
}
public void suspend() {
timer.stop();
}
public void resume() {
timer.start();
}
public void setDelay(int delay) {
this.delay = delay;
timer.setDelay(delay);
}
}
class Ball {
int x = 20;
int y = 20; // Current ball position
int dx = 2; // Increment on ball's x-coordinate
int dy = 2; // Increment on ball's y-coordinate
int radius = 15; // Ball radius
Color color = new Color((int) (Math.random() * 256),
(int) (Math.random() * 256), (int) (Math.random() * 256));
}
/** Main method */
public static void main(String[] args) {
JFrame frame = new JFrame();
JApplet applet = new MultipleBallApp();
frame.add(applet);
frame.setTitle("MultipleBallApp");
frame.setLocationRelativeTo(null); // Center the frame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
frame.setLocationRelativeTo(null); // Center the frame
frame.setVisible(true);
}
}
can't apply the same functions for ball animation
This is probably your first mistake. In fact, this is exactly what you should be trying to do. The idea is, you should be trying to devise a means by what is painted/animated is abstract so it doesn't matter what the shape is you want to paint, you can apply it to the sam basic animation process...
For example, you could start with some kind interface which describes the basic properties of an animated entity...
public interface AnimatedShape {
public void update(Rectangle bounds);
public void paint(JComponent parent, Graphics2D g2d);
}
This says that an animated entity can be updated (moved) and painted. By convention (and because I'm lazy), I like to create an abstract implementation which implements the most common aspects...
public abstract class AbstractAnimatedShape implements AnimatedShape {
private Rectangle bounds;
private int dx, dy;
public AbstractAnimatedShape() {
}
public void setBounds(Rectangle bounds) {
this.bounds = bounds;
}
public Rectangle getBounds() {
return bounds;
}
public int getDx() {
return dx;
}
public int getDy() {
return dy;
}
public void setDx(int dx) {
this.dx = dx;
}
public void setDy(int dy) {
this.dy = dy;
}
#Override
public void update(Rectangle parentBounds) {
Rectangle bounds = getBounds();
int dx = getDx();
int dy = getDy();
bounds.x += dx;
bounds.y += dy;
if (bounds.x < parentBounds.x) {
bounds.x = parentBounds.x;
setDx(dx *= -1);
} else if (bounds.x + bounds.width > parentBounds.x + parentBounds.width) {
bounds.x = parentBounds.x + (parentBounds.width - bounds.width);
setDx(dx *= -1);
}
if (bounds.y < parentBounds.y) {
bounds.y = parentBounds.y;
setDy(dy *= -1);
} else if (bounds.y + bounds.height > parentBounds.y + parentBounds.height) {
bounds.y = parentBounds.y + (parentBounds.height - bounds.height);
setDy(dy *= -1);
}
}
}
And then start creating implementations...
public class AnimatedBall extends AbstractAnimatedShape {
private Color color;
public AnimatedBall(int x, int y, int radius, Color color) {
setBounds(new Rectangle(x, y, radius * 2, radius * 2));
this.color = color;
setDx(Math.random() > 0.5 ? 2 : -2);
setDy(Math.random() > 0.5 ? 2 : -2);
}
public Color getColor() {
return color;
}
#Override
public void paint(JComponent parent, Graphics2D g2d) {
Rectangle bounds = getBounds();
g2d.setColor(getColor());
g2d.fillOval(bounds.x, bounds.y, bounds.width, bounds.height);
}
}
In this manner, you can customise the way that the entity is animated and painted, but the basic logic for each instance of the entity is the same...
But what's all the point of this...
Basically, what it allows us to do is produce a "virtual" concept of all the animated objects and simplify there management, for example...
Instead of using a "tightly" coupled List, we can use a loosely couple List instead...
private ArrayList<AnimatedShape> list = new ArrayList<AnimatedShape>();
Then when we want the entities to be updated, we simply need to iterate the List and ask the entities to update...
protected Timer timer = new Timer(delay, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (AnimatedShape ball : list) {
ball.update(getBounds());
}
repaint();
}
});
And when they need to be painted...
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (AnimatedShape ball : list) {
ball.paint(this, g2d);
}
}
Because the BallPane doesn't care what actually type of entity it is, but only that it's a type of AnimatedShape...makes life easier...
Now, my implementation of the AnimatedBall already randomise the direction of each instance of the ball, but you can also randomise the starting position when the ball is added using something like...
public void add() {
int radius = 15;
// Randomised position
int x = (int)(Math.random() * (getWidth() - (radius * 2))) + radius;
int y = (int)(Math.random() * (getHeight() - (radius * 2))) + radius;
Color color = new Color((int) (Math.random() * 256),
(int) (Math.random() * 256), (int) (Math.random() * 256));
AnimatedBall ball = new AnimatedBall(x, y, radius, color);
list.add(ball);
}
But how does this help you with adding a rectangle?
You now need to create an AnimatedRectangle that extends from AbstractAnimatedShape and implemented the required methods and add instances of this to the List of AnimatedShapes in the BallPane.
If you don't want the rectangle to be managed within the same list, you could create another list and manage it sepearatly (it create two additional methods, update(List<AnimatedShape>) and paint(List<AnimatedShape>, Graphics2D) passing in each individual list so as to reduce the duplicate code, but that's me)...
You can restrict the rectangles vertical movement by overriding the setDy method and ignoring any changes, for example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class MultipleBall {
public MultipleBall() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("MultipleBallApp");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new BallControl());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class BallControl extends JPanel {
private BallPanel ballPanel = new BallPanel();
private JButton Suspend = new JButton("Suspend");
private JButton Resume = new JButton("Resume");
private JButton Add = new JButton("+1");
private JButton Subtract = new JButton("-1");
private JScrollBar Delay = new JScrollBar();
public BallControl() {
// Group buttons in a panel
JPanel panel = new JPanel();
panel.add(Suspend);
panel.add(Resume);
panel.add(Add);
panel.add(Subtract);
// Add ball and buttons to the panel
ballPanel.setBorder(new javax.swing.border.LineBorder(Color.red));
Delay.setOrientation(JScrollBar.HORIZONTAL);
ballPanel.setDelay(Delay.getMaximum());
setLayout(new BorderLayout());
add(Delay, BorderLayout.NORTH);
add(ballPanel, BorderLayout.CENTER);
add(panel, BorderLayout.SOUTH);
// Register listeners
Suspend.addActionListener(new Listener());
Resume.addActionListener(new Listener());
Add.addActionListener(new Listener());
Subtract.addActionListener(new Listener());
Delay.addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e) {
ballPanel.setDelay(Delay.getMaximum() - e.getValue());
}
});
}
class Listener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == Suspend) {
ballPanel.suspend();
} else if (e.getSource() == Resume) {
ballPanel.resume();
} else if (e.getSource() == Add) {
ballPanel.add();
} else if (e.getSource() == Subtract) {
ballPanel.subtract();
}
}
}
}
class BallPanel extends JPanel {
private int delay = 30;
private ArrayList<AnimatedShape> list = new ArrayList<AnimatedShape>();
private AnimatedRectange rectangle;
public BallPanel() {
this.rectangle = new AnimatedRectange(-25, 200, 50, 25, Color.RED);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
// Create a timer with the initial delay
protected Timer timer = new Timer(delay, new ActionListener() {
/**
* Handle the action event
*/
#Override
public void actionPerformed(ActionEvent e) {
for (AnimatedShape ball : list) {
ball.update(getBounds());
}
rectangle.update(getBounds());
repaint();
}
});
public void add() {
int radius = 15;
// Randomised position
int x = (int) (Math.random() * (getWidth() - (radius * 2))) + radius;
int y = (int) (Math.random() * (getHeight() - (radius * 2))) + radius;
Color color = new Color((int) (Math.random() * 256),
(int) (Math.random() * 256), (int) (Math.random() * 256));
AnimatedBall ball = new AnimatedBall(x, y, radius, color);
list.add(ball);
}
public void subtract() {
if (list.size() > 0) {
list.remove(list.size() - 1); // Remove the last ball
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (AnimatedShape ball : list) {
ball.paint(this, g2d);
}
rectangle.paint(this, g2d);
}
public void suspend() {
timer.stop();
}
public void resume() {
timer.start();
}
public void setDelay(int delay) {
this.delay = delay;
timer.setDelay(delay);
}
}
public interface AnimatedShape {
public void update(Rectangle bounds);
public void paint(JComponent parent, Graphics2D g2d);
}
public abstract class AbstractAnimatedShape implements AnimatedShape {
private Rectangle bounds;
private int dx, dy;
public AbstractAnimatedShape() {
}
public void setBounds(Rectangle bounds) {
this.bounds = bounds;
}
public Rectangle getBounds() {
return bounds;
}
public int getDx() {
return dx;
}
public int getDy() {
return dy;
}
public void setDx(int dx) {
this.dx = dx;
}
public void setDy(int dy) {
this.dy = dy;
}
#Override
public void update(Rectangle parentBounds) {
Rectangle bounds = getBounds();
int dx = getDx();
int dy = getDy();
bounds.x += dx;
bounds.y += dy;
if (bounds.x < parentBounds.x) {
bounds.x = parentBounds.x;
setDx(dx *= -1);
} else if (bounds.x + bounds.width > parentBounds.x + parentBounds.width) {
bounds.x = parentBounds.x + (parentBounds.width - bounds.width);
setDx(dx *= -1);
}
if (bounds.y < parentBounds.y) {
bounds.y = parentBounds.y;
setDy(dy *= -1);
} else if (bounds.y + bounds.height > parentBounds.y + parentBounds.height) {
bounds.y = parentBounds.y + (parentBounds.height - bounds.height);
setDy(dy *= -1);
}
}
}
public class AnimatedBall extends AbstractAnimatedShape {
private Color color;
public AnimatedBall(int x, int y, int radius, Color color) {
setBounds(new Rectangle(x, y, radius * 2, radius * 2));
this.color = color;
setDx(Math.random() > 0.5 ? 2 : -2);
setDy(Math.random() > 0.5 ? 2 : -2);
}
public Color getColor() {
return color;
}
#Override
public void paint(JComponent parent, Graphics2D g2d) {
Rectangle bounds = getBounds();
g2d.setColor(getColor());
g2d.fillOval(bounds.x, bounds.y, bounds.width, bounds.height);
}
}
public class AnimatedRectange extends AbstractAnimatedShape {
private Color color;
public AnimatedRectange(int x, int y, int width, int height, Color color) {
setBounds(new Rectangle(x, y, width, height));
this.color = color;
setDx(2);
}
// Don't want to adjust the vertical speed
#Override
public void setDy(int dy) {
}
#Override
public void paint(JComponent parent, Graphics2D g2d) {
Rectangle bounds = getBounds();
g2d.setColor(color);
g2d.fill(bounds);
}
}
/**
* Main method
*/
public static void main(String[] args) {
new MultipleBall();
}
}
Amendment
You really should avoid adding JApplet to a JFrame, an applet has a prescribed life cycle and management process which you are ignoring. Better to focus on just using the BallControl panel as the core UI element and then add this to what ever top level container you want
You may find a JSlider more piratical then a JScrollBar, not to mention, it will look better on different platforms, most uses understand what a slider is used for...
Add a static variable like ballCount and add 1 to it every time you make a ball. In the Ball class, change the definition of y to something likey = 20 + ballcount*(radius*2+distanceInBalls)
public class RandomTests extends JApplet {
public RandomTests() {
add(new BallControl());
}
static int ballCount = 0;
class BallControl extends JPanel {
private BallPanel ballPanel = new BallPanel();
private JButton Suspend = new JButton("Suspend");
private JButton Resume = new JButton("Resume");
private JButton Add = new JButton("+1");
private JButton Subtract = new JButton("-1");
private JScrollBar Delay = new JScrollBar();
public BallControl() {
// Group buttons in a panel
JPanel panel = new JPanel();
panel.add(Suspend);
panel.add(Resume);
panel.add(Add);
panel.add(Subtract);
// Add ball and buttons to the panel
ballPanel.setBorder(new javax.swing.border.LineBorder(Color.red));
Delay.setOrientation(JScrollBar.HORIZONTAL);
ballPanel.setDelay(Delay.getMaximum());
setLayout(new BorderLayout());
add(Delay, BorderLayout.NORTH);
add(ballPanel, BorderLayout.CENTER);
add(panel, BorderLayout.SOUTH);
// Register listeners
Suspend.addActionListener(new Listener());
Resume.addActionListener(new Listener());
Add.addActionListener(new Listener());
Subtract.addActionListener(new Listener());
Delay.addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e) {
ballPanel.setDelay(Delay.getMaximum() - e.getValue());
}
});
}
class Listener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == Suspend) ballPanel.suspend();
else if (e.getSource() == Resume) ballPanel.resume();
else if (e.getSource() == Add) ballPanel.add();
else if (e.getSource() == Subtract) ballPanel.subtract();
}
}
}
class BallPanel extends JPanel {
private int delay = 30;
private ArrayList<Ball> list = new ArrayList<Ball>();
// Create a timer with the initial delay
protected Timer timer = new Timer(delay, new ActionListener() {
/** Handle the action event */
public void actionPerformed(ActionEvent e) {
repaint();
}
});
public BallPanel() {
timer.start();
}
public void add() {
list.add(new Ball());
ballCount++;
}
public void subtract() {
if (list.size() > 0) list.remove(list.size() - 1); // Remove the last ball
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(185, 279, 50, 15);
g.setColor(Color.RED);
g.fillRect(185, 279, 50, 15);
for (int i = 0; i < list.size(); i++) {
Ball ball = (Ball) list.get(i); // Get a ball
g.setColor(ball.color); // Set ball color
// Check boundaries
if (ball.x < 0 || ball.x > getWidth()) ball.dx = -ball.dx;
if (ball.y < 0 || ball.y > getHeight()) ball.dy = -ball.dy;
// Adjust ball position
ball.x += ball.dx;
// ball.y += ball.dy;
g.fillOval(ball.x - ball.radius, ball.y - ball.radius, ball.radius * 2, ball.radius * 2);
}
}
public void suspend() {
timer.stop();
}
public void resume() {
timer.start();
}
public void setDelay(int delay) {
this.delay = delay;
timer.setDelay(delay);
}
}
class Ball {
int radius = 15; // Ball radius
int x = radius;
int y = 20 + (radius * ballCount * 2 + 15); // Current ball position
int dx = 2; // Increment on ball's x-coordinate
int dy = 2; // Increment on ball's y-coordinate
Color color = new Color((int) (Math.random() * 256), (int) (Math.random() * 256), (int) (Math.random() * 256));
}
public static void main(String[] args) {
JFrame frame = new JFrame();
JApplet applet = new RandomTests();
frame.add(applet);
frame.setTitle("MultipleBallApp");
frame.setLocationRelativeTo(null); // Center the frame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
frame.setLocationRelativeTo(null); // Center the frame
frame.setVisible(true);
}
}

Swing custom component dummy mouselistener

I am just starting to put together a logging tool for my own use that would log statistics from gym/running and the only experience I have with swing/awt is active rendering for games where you have full control over the Graphics2D object and don't rely on implementing swing components with overriden paints.
Anyway, I was hoping to create a dummy JComponent that I can add to one of my panels (this panel will display graphics, statistics etc depending on what I select from another different sidepanel with options) that does nothing else but listen for mouseevents inside the panel mentioned earlier and draws a selection rectangle on mousedrags so that I can zoom in the data if higher resolutions exist. I just don't know how, I have added the component to the panel but it registers nothing inside the panel, instead it seems to have a local space that is limited to the bottom of the panel/frame.
Here is the component
package gui;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JComponent;
#SuppressWarnings("serial")
public class MarkerRectangle extends JComponent implements MouseListener,
MouseMotionListener {
private boolean draw;
private int startX, endX, startY, endY;
private Color color = new Color(0, 255, 0, 100);
public MarkerRectangle(int width, int height) {
setPreferredSize(new Dimension(width, height));
this.addMouseListener(this);
this.addMouseMotionListener(this);
}
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("Mouse clicked# " + e.getX() + "," + e.getY());
}
#Override
public void mouseEntered(MouseEvent e) {
System.out.println("Mouse entered # " + e.getX() + "," + e.getY());
}
#Override
public void mouseExited(MouseEvent e) {
System.out.println("Mouse left # " + e.getX() + "," + e.getY());
}
#Override
public void mousePressed(MouseEvent e) {
System.out.println("Mouse pressed# " + e.getX() + "," + e.getY());
}
#Override
public void mouseReleased(MouseEvent e) {
System.out.println("Mouse was released # " + e.getX() + "," + e.getY());
}
#Override
public void mouseDragged(MouseEvent e) {
System.out.println("Mouse being dragged, currently# " + e.getX() + ","
+ e.getY());
}
#Override
public void mouseMoved(MouseEvent e) {
System.out.println("I registered a move# " + e.getX() + "," + e.getY());
}
#Override
// Draw rectangle
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.dispose();
}
}
The panel
public class GraphPane extends JPanel {
public GraphPane(Graph graph) {
this.add(graph);
this.add(new MarkerRectangle(800, 600));
this.setBackground(Color.gray);
this.setPreferredSize(new Dimension(800, 600));
}
}
The graph, holds random data atm
public class Graph extends JComponent {
private GeneralPath data;
private Stroke stroke;
public Graph() {
this.data = new GeneralPath();
this.stroke = new BasicStroke(3f);
this.setPreferredSize(new Dimension(750, 550));
init();
}
private void init() {
data.moveTo(0, 0);
double cntr = 0;
double[][] points = new double[10][1];
for (double[] point : points) {
cntr += 100;
point[0] = Math.random() * 100;
point[1] = Math.random() * 100;
data.lineTo(cntr, point[1]);
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(stroke);
g2d.draw(data);
g2d.dispose();
}
}
I want to implement something like the above because eventually I imagine the GUI to become quite complex and simple events like drawing a rectangle to mark data should not be in the main controller (so as to prevent a lot of if-tests and clutter in code).
Screenshot of what I get:
What I want:
EDIT
While the accepted answer below is the better solution I am posting this in the event that someone may want to use it. It will not work if you resize smaller the prefferedSize.
public class Test {
public static void main(String[] args) {
GeneralJFrame frame = new GeneralJFrame(1200, 800);
frame.addPanel(new GraphPane(new Graph()), BorderLayout.CENTER);
frame.addPanel(new ButtonPane(), BorderLayout.SOUTH);
frame.addPanel(new ButtonPane(), BorderLayout.SOUTH);
frame.addPanel(new ButtonPane(), BorderLayout.WEST);
frame.addPanel(new ButtonPane(), BorderLayout.EAST);
frame.addPanel(new ButtonPane(), BorderLayout.NORTH);
frame.start();
}
}
public class GraphPane extends JPanel {
public GraphPane(Graph graph) {
GridBagConstraints c = new GridBagConstraints();
c.gridwidth = GridBagConstraints.REMAINDER;
c.gridheight = GridBagConstraints.REMAINDER;
c.gridx = 0;
c.gridy = 0;
this.setLayout(new GridBagLayout());
this.add(graph);
this.add(new MarkerRectangle(), c);
this.setBackground(new Color(205, 201, 201));
}
}
public class MarkerRectangle extends JComponent implements MouseListener,
MouseMotionListener {
private boolean draw;
private int startX, endX, startY, endY;
public MarkerRectangle() {
this.addMouseListener(this);
this.addMouseMotionListener(this);
setOpaque(false);
setPreferredSize(new Dimension(800, 600));
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
startX = e.getX();
startY = e.getY();
draw = true;
}
#Override
public void mouseReleased(MouseEvent e) {
draw = false;
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
endX = e.getX();
endY = e.getY();
repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
}
#Override
// Draw rectangle
protected void paintComponent(Graphics g) {
System.out.println(getSize());
if (!draw)
return;
int w = endX-startX;
int h = endY - startY;
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(new Color(255, 165, 0));
g2d.fillRect(startX, startY, w, h);
g2d.dispose();
}
}
public class ButtonPane extends JPanel {
public ButtonPane() {
add(new JButton("HELLO"));
setBackground(Color.gray);
setBorder(BorderFactory.createEtchedBorder(Color.white,
Color.gray.darker()));
}
}
public class GeneralJFrame {
private JFrame frame;
public GeneralJFrame(int width, int height) {
this.frame = new JFrame("Le Muscles");
this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void addPanel(JPanel panel, String location) {
this.frame.add(panel, location);
}
public void start() {
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Outputs: (orange is dragged with mouse)
I still don't fully understand your question. As I see it, you've
created several JPanels
Given one JPanel a MouseListener and MouseMotionListener
You've added that JPanel to the bottom of another JPanel.
The JPanel that's sitting on the bottom registers all mouse events as it has been told to do
So your program is behaving as expected based on your code.
The question I have is this: how is it not behaving as you expect it to?
If you expect that adding a JPanel with MouseListeners and MouseMotionListeners attached will result in all the JPanels of the GUI to be listened to, well of course that won't happen. To have more of the GUI register mouse events, you'll have to add MouseListeners and MouseMotionListeners to those components. And that is my answer so far to your question as I see it. If I didn't answer the true question you currently face, then please clarify it for us.
You state:
What I want is an invisible (transparent) panel on top of the blue one in the above screenshot that is just as large as the blue one, not a subpanel that is sitting in the bottom. I want the blue one to contain this one (should not be a problem since it is just a jcomponent). What I hope to achieve is a sort over "invisible" overlay that registers mousevents so I don't have to implement these events in the blue panel.
Consider using a JLayer for this. As per the JLayer API:
JLayer is a good solution if you only need to do custom painting over compound component or catch input events from its subcomponents.
OK, I've experimented with it a bit and came up with something like this:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Path2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;
import javax.swing.*;
import javax.swing.plaf.LayerUI;
#SuppressWarnings("serial")
public class GraphPane2 extends JPanel {
private static final int GRAPH_WIDTH = 1000;
private static final int GRAPH_HEIGHT = 750;
private Graph2 graph2 = new Graph2(GRAPH_WIDTH, GRAPH_HEIGHT);
public GraphPane2() {
LayerUI<Graph2> myLayerUI = new MyLayerUI<Graph2>();
JLayer<Graph2> panelLayer = new JLayer<Graph2>(graph2, myLayerUI);
setLayout(new BorderLayout());
add(panelLayer);
myLayerUI.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (MyLayerUI.MOUSE_RELEASED.equals(evt.getPropertyName())) {
Rectangle rect = (Rectangle) evt.getNewValue();
System.out.println(rect);
}
}
});
}
private static void createAndShowGui() {
GraphPane2 mainPanel = new GraphPane2();
JFrame frame = new JFrame("Graph Pane2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.setResizable(false);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class MyLayerUI<V extends JComponent> extends LayerUI<V> {
private static final Color FILL_COLOR = new Color(0, 128, 0, 128);
public static final String MOUSE_RELEASED = "mouse released";
private Point pressedPt;
private Point draggedPt;
private Rectangle rect;
#Override
public void paint(Graphics g, JComponent c) {
super.paint(g, c);
if (rect != null) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(FILL_COLOR);
g2.fill(rect);
}
}
public void installUI(JComponent c) {
super.installUI(c);
((JLayer) c).setLayerEventMask(AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK);
}
public void uninstallUI(JComponent c) {
super.uninstallUI(c);
((JLayer)c).setLayerEventMask(0);
}
#Override
public void eventDispatched(AWTEvent e, JLayer<? extends V> l) {
MouseEvent mEvt = (MouseEvent) e;
int id = mEvt.getID();
int btn = mEvt.getButton();
if (id == MouseEvent.MOUSE_PRESSED && btn == MouseEvent.BUTTON1) {
pressedPt = mEvt.getPoint();
rect = new Rectangle(pressedPt.x, pressedPt.y, 0, 0);
}
if (id == MouseEvent.MOUSE_PRESSED && btn != MouseEvent.BUTTON1) {
pressedPt = null;
}
if (id == MouseEvent.MOUSE_DRAGGED && pressedPt != null) {
draggedPt = mEvt.getPoint();
int x = Math.min(draggedPt.x, pressedPt.x);
int y = Math.min(draggedPt.y, pressedPt.y);
int width = Math.abs(draggedPt.x - pressedPt.x);
int height = Math.abs(draggedPt.y - pressedPt.y);
rect = new Rectangle(x, y, width, height);
}
if (id == MouseEvent.MOUSE_RELEASED && pressedPt != null) {
draggedPt = mEvt.getPoint();
int x = Math.min(draggedPt.x, pressedPt.x);
int y = Math.min(draggedPt.y, pressedPt.y);
int width = Math.abs(draggedPt.x - pressedPt.x);
int height = Math.abs(draggedPt.y - pressedPt.y);
rect = new Rectangle(x, y, width, height);
firePropertyChange(MOUSE_RELEASED, null, rect);
}
l.repaint();
}
}
#SuppressWarnings("serial")
class Graph2 extends JPanel {
private static final int MAX_DATA_POINTS = 100;
private static final int STEP = 10;
private static final Stroke STROKE = new BasicStroke(3f);
private Path2D path2D;
private int width;
private int height;
private int[] data = new int[MAX_DATA_POINTS + 1];
private Random random = new Random();
public Graph2(int width, int height) {
this.width = width;
this.height = height;
init();
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
path2D = new Path2D.Double();
int w = getWidth();
int h = getHeight();
double x = 0;
double y = ((double) MAX_DATA_POINTS - data[0]) * h
/ MAX_DATA_POINTS;
path2D.moveTo(x, y);
for (int i = 1; i < data.length; i++) {
x = (i * w) / (double) MAX_DATA_POINTS;
y = ((double) MAX_DATA_POINTS - data[i]) * h
/ (double) MAX_DATA_POINTS;
path2D.lineTo(x, y);
}
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (path2D != null) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(STROKE);
g2d.draw(path2D);
}
};
private void init() {
// create and fill random data
data[0] = 0;
boolean up = true;
// max and min data values -- used for normalization
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for (int i = 1; i < data.length; i++) {
up = random.nextInt(4) < 3 ? up : !up;
if (up) {
data[i] = data[i - 1] + random.nextInt(STEP);
} else {
data[i] = data[i - 1] - random.nextInt(STEP);
}
if (data[i] > max) {
max = data[i];
}
if (data[i] < min) {
min = data[i];
}
}
// normalize the data
for (int i = 0; i < data.length; i++) {
int datum = (MAX_DATA_POINTS * (data[i] - min)) / (max - min);
data[i] = datum;
}
}
}
This will look like:

Multiple calls of paintComponent()

I have these two classes:
public class Pencil extends JComponent implements MouseListener, MouseMotionListener{
Plansa plansa;
Graphics g;
public Pencil(Plansa newCanvas){
this.plansa = newCanvas;
this.plansa.setFlagShape(false);
}
#Override
public void mouseDragged(MouseEvent arg0) {
plansa.setMouseDragged(arg0);
this.plansa.setFlagShape(false);
plansa.paintComponent(plansa.getGraphics());
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent arg0) {
plansa.setMousePressed(arg0);
}
#Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
plansa.setMouseReleased(arg0);
this.plansa.setFlagShape(true);
plansa.paintComponent(plansa.getGraphics());
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
}
}
And this one:
public class Plansa extends JPanel{
Image image;
Pencil pencil;
//...
void init(){
this.setShape("freeLine");
this.setColor(Color.BLACK);
this.size=1;
this.type="round";
this.fill=false;
this.setBackground(Color.WHITE);
pencil = new Pencil(this);
addMouseListener(pencil);
addMouseMotionListener(pencil);
flagPaint = true;
flagShape = false;
}
public Plansa(){
this.setSize(800, 600);
init();
}
//...
#Override
public void paintComponent(Graphics g) {
g.setColor(currentColor);
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(setBrush(size,type));
super.paintComponent(g);
switch(shape){
default: break;
case "freeLine":{
g.drawLine(xDragged, yDragged, xCurrent, yCurrent);
break;
}
case "rectangle":{
if(flagShape == true){
g.drawRect(xPressed, yPressed, Math.max(xCurrent-xPressed,xPressed-xCurrent), Math.max(yCurrent-yPressed,yPressed-yCurrent));
if(fill == true) g.fillRect(xPressed, yPressed, Math.max(xCurrent-xPressed,xPressed-xCurrent), Math.max(yCurrent-yPressed,yPressed-yCurrent));
}
break;
}
case "circle":{
if(flagShape == true){
int radius = (int)Math.sqrt(Math.max(xCurrent-xPressed,xPressed-xCurrent)*Math.max(xCurrent-xPressed,xPressed-xCurrent)+Math.max(yCurrent-yPressed,yPressed-yCurrent)*Math.max(yCurrent-yPressed,yPressed-yCurrent));
g.drawOval(xPressed, yPressed, radius, radius);
if(fill == true) g.fillOval(xPressed, yPressed, radius, radius);
}
break;
}
case "oval":{
if(flagShape == true){
g.drawOval(xPressed, yPressed, Math.max(xCurrent-xPressed,xPressed-xCurrent), Math.max(yCurrent-yPressed,yPressed-yCurrent));
if(fill == true) g.fillOval(xPressed, yPressed, Math.max(xCurrent-xPressed,xPressed-xCurrent), Math.max(yCurrent-yPressed,yPressed-yCurrent));
}
break;
}
case "line":{
if(flagShape == true){
g.drawLine(xPressed, yPressed, xCurrent, yCurrent);
}
break;
}
}
}
//...
}
My problem is that every time I call paintComponent() method, the JPanel clears and the only item that remains is the one I just drawn. Is there any way to avoid this?
After close inspection of the code, it appears that you're trying to use Components as "painters", it's a bit like using a sports car as a go-kart, a lot of extras for little gain.
Instead, you should define yourself a interface of some kine that provides the basic requirements of what you want to draw/paint and then define concrete class implementations that implement the functionality.
You would then maintain a List of some kind of all the drawings that can be painted.
While simple, the example below provides a jumping off point that could be enhanced that allow selection of drawings, re-ordering and removal (should you want it).
This, is, essentially, the same concept proposed by Doorknob (+1)
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestDraw {
public static void main(String[] args) {
new TestDraw();
}
public TestDraw() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new Pencil());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Pencil extends JPanel implements MouseListener, MouseMotionListener {
private List<Drawable> drawables;
private Drawable activeDrawable;
private Point clickPoint;
public Pencil() {
drawables = new ArrayList<>(5);
addMouseListener(this);
addMouseMotionListener(this);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Drawable drawable : drawables) {
drawable.paint(g2d);
}
g2d.dispose();
}
#Override
public void mouseDragged(MouseEvent e) {
if (activeDrawable != null) {
Point p = e.getPoint();
Rectangle bounds = activeDrawable.getBounds();
int x = bounds.x;
int y = bounds.y;
int width = p.x - clickPoint.x;
int height = p.y - clickPoint.y;
if (width < 0) {
width *= -1;
x = p.x;
}
if (height < 0) {
height *= -1;
y = p.y;
}
bounds = new Rectangle(x, y, width, height);
System.out.println(bounds);
activeDrawable.setBounds(bounds);
repaint();
}
}
#Override
public void mouseClicked(MouseEvent e) {
}
protected Drawable createActiveShape(MouseEvent e) {
System.out.println("Anchor = " + e.getPoint());
Drawable drawable = new FreeLine(e.getPoint());
drawable.setLocation(e.getPoint());
return drawable;
}
#Override
public void mousePressed(MouseEvent e) {
// You could also check to see if the clicked on a drawable...
clickPoint = e.getPoint();
activeDrawable = createActiveShape(e);
drawables.add(activeDrawable);
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
if (activeDrawable != null) {
Rectangle bounds = activeDrawable.getBounds();
if (bounds.width == 0 || bounds.height == 0) {
drawables.remove(activeDrawable);
}
}
clickPoint = null;
activeDrawable = null;
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
}
}
public interface Drawable {
public void setLocation(Point p);
public void setSize(Dimension dim);
public void setBounds(Rectangle bounds);
public Rectangle getBounds();
public void paint(Graphics2D g2d);
}
public abstract class AbstractDrawable implements Drawable {
private Rectangle bounds;
public AbstractDrawable() {
bounds = new Rectangle();
}
#Override
public void setLocation(Point p) {
bounds.setLocation(p);
}
#Override
public void setBounds(Rectangle bounds) {
this.bounds = bounds;
}
#Override
public void setSize(Dimension dim) {
bounds.setSize(dim);
}
#Override
public Rectangle getBounds() {
return bounds;
}
}
public class FreeLine extends AbstractDrawable {
private Point anchor;
public FreeLine(Point anchor) {
this.anchor = anchor;
}
#Override
public void paint(Graphics2D g2d) {
Rectangle bounds = getBounds();
Point p1 = new Point(anchor);
Point p2 = new Point(bounds.getLocation());
if (p1.x > p2.x) {
p2.x = p1.x - bounds.width;
} else {
p2.x = p1.x + bounds.width;
}
if (p1.y > p2.y) {
p2.y = p1.y - bounds.height;
} else {
p2.y = p1.y + bounds.height;
}
g2d.draw(new Line2D.Float(p1, p2));
}
}
Store all the objects in an ArrayList and iterate through it when you are drawing.
You could have them all implement a custom interface, Drawable for example, then store in an ArrayList<Drawable>.
A better idea generally for progressive drawings is to use a BufferedImage as the canvas, as seen here.
Don't use/comment out:
super.paintComponent(g);
That line is the one that is doing the clearing.

Components disappear after resizing JPanel

I am trying to create JPanel with draggable crosses which appear after mouse clicking. Everything works fine but when I resize the JPanel the crosses disappear. I tried to override the paintComponent method in my JPanel but then all crosses are at coordinates (0,0). How can I fix it?
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
import java.util.ArrayList;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class CrossPanel extends JPanel implements MouseListener {
private int orderOfCross = 0;
private ArrayList<Cross> crosses;
private int defaultSizeOfCrosses = 10;
CrossPanel() {
setOpaque(false);
addMouseListener(this);
crosses = new ArrayList<Cross>();
}
#Override
public void mouseClicked(MouseEvent e) {
int x = e.getX();
int y = e.getY();
Cross cross = new Cross(orderOfCross++, defaultSizeOfCrosses);
crosses.add(cross);
cross.setLocation(x - defaultSizeOfCrosses, y - defaultSizeOfCrosses);
add(cross);
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// for (int i = 0; i < crosses.size(); i++) {
// crosses.get(i).paint(g);
// }
}
#Override
public void mousePressed(MouseEvent e) {}
#Override
public void mouseReleased(MouseEvent e) {}
#Override
public void mouseEntered(MouseEvent e) {}
#Override
public void mouseExited(MouseEvent e) {}
public static void main(String[] args) {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
CrossPanel crossPane = new CrossPanel();
f.getContentPane().add(crossPane);
f.setSize(600, 500);
f.setLocation(200, 200);
f.setVisible(true);
}
}
class Cross extends JComponent {
private int order;
protected Cursor draggingCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
private volatile int draggedAtX, draggedAtY;
int size;
public Cross(int order, int size) {
this.order = order;
this.size = size;
this.setBounds(0, 0, 4 * size, 3 * size + 10);
addDragListeners();
setCursor(draggingCursor);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.red);
g2.setStroke(new BasicStroke(3));
g2.drawLine(0, size, size + size, size);
g2.drawLine(size, 0, size, size + size);
Font f = new Font("Monospaced", Font.BOLD, size + 10);
g2.setFont(f);
g2.drawString(String.valueOf(order), size - size / 2, 3 * size + 10);
}
private void addDragListeners() {
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
draggedAtX = e.getX();
draggedAtY = e.getY();
}
});
addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
Point newLocation = new Point(e.getX() - draggedAtX + getLocation().x, e.getY() - draggedAtY + getLocation().y);
setLocation(newLocation);
}
});
}
}
I very rarely see a use for a null layout, for all the perceived benefits, there are simply to many draw backs.
The entire Swing API has been designed around the use of layout managers so you'd be crazy (IMHO) to simply throw all that work away.
If you find yourself in a position where the available layout managers don't seem to do what you want, it might be more worth while to write you own.
Here, I've presented a PropertionalLayoutManager whose intention is to provide layout capabilities that will place components on a container based an percentage of the width/height of the parent component. This means, as the parent component is resized, the child components will reposition themselves at a percentage of the parent size.
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.LayoutManager2;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class CrossPanel extends JPanel implements MouseListener {
private int orderOfCross = 0;
private ArrayList<Cross> crosses;
private int defaultSizeOfCrosses = 10;
CrossPanel() {
setOpaque(false);
addMouseListener(this);
crosses = new ArrayList<Cross>();
setLayout(new PropertionalLayoutManager());
}
#Override
public void mouseClicked(MouseEvent e) {
int x = e.getX();
int y = e.getY();
Cross cross = new Cross(orderOfCross++, defaultSizeOfCrosses);
float xPos = (float)x / (float)getWidth();
float yPos = (float)y / (float)getHeight();
crosses.add(cross);
add(cross, new PropertionalConstraints(xPos, yPos));
revalidate();
}
public static String format(float value) {
return NumberFormat.getNumberInstance().format(value);
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
public static void main(String[] args) {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
CrossPanel crossPane = new CrossPanel();
f.getContentPane().add(crossPane);
f.setSize(600, 500);
f.setLocation(200, 200);
f.setVisible(true);
}
public class Cross extends JComponent {
private int order;
protected Cursor draggingCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
private volatile int draggedAtX, draggedAtY;
int size;
public Cross(int order, int size) {
this.order = order;
this.size = size;
// this.setBounds(0, 0, 4 * size, 3 * size + 10);
addDragListeners();
setCursor(draggingCursor);
Font f = new Font("Monospaced", Font.BOLD, size + 10);
setFont(f);
}
#Override
public Dimension getPreferredSize() {
// This is dangrous, you are making assumptions about platforms
// that you have no eviednce to support.
FontMetrics fm = getFontMetrics(getFont());
return new Dimension(Math.max(fm.stringWidth(String.valueOf(order)), size), size + fm.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.red);
g2.setStroke(new BasicStroke(3));
FontMetrics fm = g2.getFontMetrics();
int width = getWidth() - 1;
int height = getHeight() - 1;
int x = (width - fm.stringWidth(String.valueOf(order))) / 2;
int y = fm.getAscent();
g2.drawString(String.valueOf(order), x, y);
int crossSize = Math.min(width, height - fm.getHeight());
x = (width - crossSize) / 2;
y = fm.getHeight();
g2.drawLine(x, y, x + crossSize, y + crossSize);
g2.drawLine(x + crossSize, y, x, y + crossSize);
}
private void addDragListeners() {
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
draggedAtX = e.getX();
draggedAtY = e.getY();
}
});
addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
Point newLocation = new Point(e.getX() - draggedAtX + getLocation().x, e.getY() - draggedAtY + getLocation().y);
setLocation(newLocation);
}
});
}
}
public class PropertionalConstraints {
private float x;
private float y;
public PropertionalConstraints(float x, float y) {
this.x = x;
this.y = y;
}
public float getX() {
return x;
}
public float getY() {
return y;
}
public void setX(float x) {
if (x > 1f) {
x = 1f;
} else if (x < -0f) {
x = 0f;
}
this.x = x;
}
public void setY(float y) {
if (y > 1f) {
y = 1f;
} else if (y < -0f) {
y = 0f;
}
this.y = y;
}
}
public class PropertionalLayoutManager implements LayoutManager2 {
private Map<Component, PropertionalConstraints> mapConstraints;
public PropertionalLayoutManager() {
mapConstraints = new HashMap<>(25);
}
public PropertionalConstraints getConstraintsFor(Component comp) {
return mapConstraints.get(comp);
}
public void setConstraintsFor(Component comp, PropertionalConstraints pc) {
mapConstraints.put(comp, pc);
}
#Override
public void addLayoutComponent(Component comp, Object constraints) {
if (constraints instanceof PropertionalConstraints) {
mapConstraints.put(comp, (PropertionalConstraints) constraints);
} else {
throw new IllegalArgumentException("Constraints must be PropertionalConstraints");
}
}
#Override
public Dimension maximumLayoutSize(Container target) {
return preferredLayoutSize(target);
}
#Override
public float getLayoutAlignmentX(Container target) {
return 0.5f;
}
#Override
public float getLayoutAlignmentY(Container target) {
return 0.5f;
}
#Override
public void invalidateLayout(Container target) {
}
#Override
public void addLayoutComponent(String name, Component comp) {
}
#Override
public void removeLayoutComponent(Component comp) {
mapConstraints.remove(comp);
}
#Override
public Dimension preferredLayoutSize(Container parent) {
return parent.getSize();
}
#Override
public Dimension minimumLayoutSize(Container parent) {
return preferredLayoutSize(parent);
}
#Override
public void layoutContainer(Container parent) {
int width = parent.getWidth();
int height = parent.getHeight();
for (Component comp : parent.getComponents()) {
PropertionalConstraints con = mapConstraints.get(comp);
if (con != null) {
int x = (int)(width * con.getX());
int y = (int)(height * con.getY());
comp.setSize(comp.getPreferredSize());
comp.setLocation(x, y);
} else {
comp.setBounds(0, 0, 0, 0);
}
}
}
}
}
On some side notes, you are using "magic" numbers to determine the size and rendering position of certain elements. This is a very bad idea. You should, especially when painting or printing, base all these values on empirical values.
In this example, I've reverted to using FontMertrics to provide the required information to more accurately calculate the size and positions of various elements. This will allow for better cross platform support, as not all fonts are rendered the same across all platforms ;)

Categories

Resources