I created an app that contains a square that bounces every time it touches an edge of the frame.I don't have issues lunching the app,the problem is that i don't know how to create various threads in order to have multiples squares inside the frame.
I tried multiple things but i can't figure out where i should create the threads.
I also noticed that the square is visible only when i add it directly inside the frame and not when i put it inside a JPanel.
Square.java
public class Square extends JComponent implements ActionListener {
int width = 20;
int height = 20;
double y = Math.random() * 360;
double x = Math.random() * 360;
boolean xMax = false;
boolean yMax = false;
boolean xMin = true;
boolean yMin = true;
Rectangle2D.Double square = new Rectangle2D.Double(x, y, width, height);
public Square() {
Timer t = new Timer(2, this);
t.start();
}
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
super.paintComponent(g);
g2.setColor(Color.BLUE);
g2.fill(square);
x_y_rules();
}
public void x_y_rules() {
if (xMax == true) {
x = x - 0.5;
if (x <= 0) {
xMax = false;
}
} else {
x = x + 0.5;
if (x >= this.getWidth()) {
xMax = true;
}
}
if (yMax == true) {
y = y - 0.5;
if (y <= 0) {
yMax = false;
}
} else {
y = y + 0.5;
if (y >= this.getHeight()) {
yMax = true;
}
}
square.setFrame(x, y, width, height);
}
#Override
public void actionPerformed(ActionEvent arg0) {
repaint();
}
}
App.java
public class App extends JFrame {
public static void main(String[] args) {
JFrame jf = new JFrame();
Square sqr = new Square();
jf.setSize(400, 400);
jf.setVisible(true);
jf.add(sqr);
jf.setDefaultCloseOperation(EXIT_ON_CLOSE);
jf.setLocationRelativeTo(null);
}
}
Is it normal that despite i put a time of 2 inside the Timer,the square moves very slowly?
Issues:
you've got program logic, the x_y_rules() method call, inside of the paintComponent method. Get it out as it does not belong there, and instead put it into the Timer's ActionListener code where it belongs.
you can give each Square its own Swing Timer if you want. This isn't really a threading issue since each Timer's ActionListener will run on the EDT.
Two milliseconds is an unrealistic time slice to expect to use in a Swing Timer and no timer will run that fast. 11 to 13 is about the fastest to expect or hope for.
if you want your sprite to move faster, give it a greater value for delta-x and delta-y in your movement code.
Your JComponent has no preferred size defined which is likely why it's not showing up in the JPanel, since the default FlowLayout will size it then to [0, 0]. Override its getPreferredSize() and have it return a reasonable Dimension value.
you're calling setVisible(true) on your JFrame before adding all components, a no-no.
Ok,i put a getPrefferedSize() inside the square class but i've encountered a problem: the squares are not "together",it's like they're bouncing on separate panels
Then your program structure is broken. You really don't want create separate Swing components, and in fact your Square class shouldn't extend JComponent or JPanel. Rather
Square should be a logical class, one that extends from nothing (other than default Object).
Give it a drawing method, say public void draw(Graphics g) {....}
Create one class that extends JPanel, say called DrawingPanel, and override its paintComponent method.
Give the DrawingPanel class an ArrayList<Square> so that it can hold multiple Square objects.
Give the DrawingPanel class a Swing Timer
In the DrawingPanel class's Timer, have it update the position of all the Squares in the ArrayList, and then call repaint()
In the paintComponent method, iterate through all the Squares in the list, using a for loop, and call each one's draw method.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawingPanel extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
private static final int TIMER_DELAY = 20;
private static final Color[] SQUARE_COLOR = { Color.BLUE, Color.CYAN, Color.DARK_GRAY,
Color.BLACK, Color.GRAY, Color.GREEN, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE,
Color.PINK, Color.RED, Color.YELLOW };
List<Square> squareList = new ArrayList<>();
public DrawingPanel() {
// create a bunch of squares
for (int i = 0; i < SQUARE_COLOR.length; i++) {
squareList.add(new Square(SQUARE_COLOR[i], PREF_W, PREF_H));
}
setBackground(Color.WHITE);
// create and start the timer
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// simply draw all the squares in the list
for (Square square : squareList) {
square.draw(g);
}
}
// set size of JPanel
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
// simply iterate through list and move all squares
for (Square square : squareList) {
square.move();
}
repaint(); // then repaint the GUI
}
}
private static void createAndShowGui() {
DrawingPanel mainPanel = new DrawingPanel();
JFrame frame = new JFrame("Drawing Panel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
// this class does *not* extend JPanel or JComponent
class Square {
public static final int WIDTH = 20;
// location of Square
private double sqrX;
private double sqrY;
// X and Y speed
private double deltaX;
private double deltaY;
// width and height of DrawingPanel JPanel
private int dpWidth;
private int dpHeight;
// image to draw
private Image image;
public Square(Color color, int dpWidth, int dpHeight) {
this.dpWidth = dpWidth;
this.dpHeight = dpHeight;
// create square at random location with random speed
sqrX = Math.random() * (dpWidth - WIDTH);
sqrY = Math.random() * (dpHeight - WIDTH);
deltaX = Math.random() * 10 - 5;
deltaY = Math.random() * 10 - 5;
// one way to draw it is to create an image and draw it
image = new BufferedImage(WIDTH, WIDTH, BufferedImage.TYPE_INT_ARGB);
Graphics g = image.getGraphics();
g.setColor(color);
g.fillRect(0, 0, WIDTH, WIDTH);
g.dispose();
}
public void move() {
// check that we're not hitting boundaries
if (sqrX + deltaX < 0) {
deltaX = Math.abs(deltaX);
}
if (sqrX + deltaX + WIDTH >= dpWidth) {
deltaX = -Math.abs(deltaX);
}
sqrX += deltaX;
// check that we're not hitting boundaries
if (sqrY + deltaY < 0) {
deltaY = Math.abs(deltaY);
}
if (sqrY + deltaY + WIDTH >= dpHeight) {
deltaY = -Math.abs(deltaY);
}
sqrY += deltaY;
}
public void draw(Graphics g) {
int x = (int) sqrX;
int y = (int) sqrY;
g.drawImage(image, x, y, null);
}
}
Related
I am trying to rotate a circle around a separate point in a program. right now I can get the circle to rotate but it slowly starts getting closer and closer to the point it's rotating from. I am trying to do this using JPanel and implementing it as a rectangle.
package WoffindenZone;
import java.awt.*;
import javax.swing.*;
import java.util.*;
import java.awt.event.*;
import java.lang.Math;
public class Protector extends Rectangle{
double Velocity;
int speed = 3;
Protector(int x, int y, int PROTECTOR_DIAMETER){
super(x,y,PROTECTOR_DIAMETER,PROTECTOR_DIAMETER);
}
public void keyPressed(KeyEvent e){
if(e.getKeyCode()==KeyEvent.VK_A) {
setDirection(speed);
move();
}
if(e.getKeyCode()==KeyEvent.VK_D) {
setDirection(speed);
move();
}
}
public void keyReleased(KeyEvent e){
if(e.getKeyCode()==KeyEvent.VK_A) {
setDirection(0);
move();
}
if(e.getKeyCode()==KeyEvent.VK_D) {
setDirection(0);
move();
}
}
public void setDirection(int Direction){
Velocity = Direction*Math.PI/180;
}
public void move(){
x = (int)Math.round(500 + Math.cos(Velocity) * (x-500) - Math.sin(Velocity) * (y-((1000*0.5555)/2)));
y = (int)Math.round(((1000*0.5555)/2) + Math.sin(Velocity) * (x-500) + Math.cos(Velocity) * (y-((1000*0.5555)/2)));
System.out.println(x);
System.out.println(y);
}
public void draw(Graphics g){
g.setColor(Color.blue);
g.fillOval(x,y,width,height);
}
Use a rotation instance of an AffineTransform. See getRotateInstance(theta,anchorx,anchory) for details.
Returns a transform that rotates coordinates around an anchor point. This operation is equivalent to translating the coordinates so that the anchor point is at the origin (S1), then rotating them about the new origin (S2), and finally translating so that the intermediate origin is restored to the coordinates of the original anchor point (S3).
How do you rotate a circle about a point in JPanel?
Here's how I rotate a circle about a point in a JPanel.
I don't know how to make an animated GIF. Just imagine the blue circle rotating clockwise around the center of the drawing JPanel.
So, let's start at the beginning. Basically, I have a circle rotating on the circumference of another circle. So, I create a Circle model class from plain Java.
public class Circle {
private final int radius;
private final Color color;
private Point center;
public Circle(int radius, Color color) {
this.radius = radius;
this.color = color;
}
public Point calculateCircumferencePoint(int theta) {
double radians = Math.toRadians(theta);
int x = center.x + (int) Math.round(Math.cos(radians) * radius);
int y = center.y + (int) Math.round(Math.sin(radians) * radius);
return new Point(x, y);
}
public void setCenter(int x, int y) {
this.center = new Point(x, y);
}
public void setCenter(Point center) {
this.center = center;
}
public int getRadius() {
return radius;
}
public Color getColor() {
return color;
}
public Point getCenter() {
return center;
}
}
The class consists of basic getters and setters. I make the radius and color final because they don't change value in this Java application.
The calculateCircumferencePoint method is the only interesting method. It takes an int angle in degrees, and calculates the point on the circumference represented by that angle, rounded to the nearest X and Y integer points.
Next, we create two Circle instances, an inner circle and an outer circle. Here's the class constructor setting the preferred size of the drawing area, the inner circle, and the outer circle. We start the outer circle at zero degrees (to the right);
private Circle innerCircle;
private Circle outerCircle;
private Dimension drawingPanelSize;
public RotateCircle() {
this.drawingPanelSize = new Dimension(400, 400);
int innerCircleRadius = drawingPanelSize.width / 4;
int centerX = drawingPanelSize.width / 2;
int centerY = drawingPanelSize.height / 2;
int outerCircleRadius = drawingPanelSize.width / 10;
this.innerCircle = new Circle(innerCircleRadius, null);
this.innerCircle.setCenter(centerX, centerY);
this.outerCircle = new Circle(outerCircleRadius, Color.BLUE);
Point point = innerCircle.calculateCircumferencePoint(0);
this.outerCircle.setCenter(point);
}
Now, we can start coding the GUI. First, we start the Java application by calling the SwingUtilities invokeLater method. This method ensures that we create and execute the Swing components on the Event Dispatch Thread.
Next, we define the JFrame. Here's the code we have so far.
public static void main(String[] args) {
SwingUtilities.invokeLater(new RotateCircle());
}
private Animation animation;
private Circle innerCircle;
private Circle outerCircle;
private DrawingPanel drawingPanel;
private Dimension drawingPanelSize;
public RotateCircle() {
this.drawingPanelSize = new Dimension(400, 400);
int innerCircleRadius = drawingPanelSize.width / 4;
int centerX = drawingPanelSize.width / 2;
int centerY = drawingPanelSize.height / 2;
int outerCircleRadius = drawingPanelSize.width / 10;
this.innerCircle = new Circle(innerCircleRadius, null);
this.innerCircle.setCenter(centerX, centerY);
this.outerCircle = new Circle(outerCircleRadius, Color.BLUE);
Point point = innerCircle.calculateCircumferencePoint(0);
this.outerCircle.setCenter(point);
}
#Override
public void run() {
JFrame frame = new JFrame("Rotate Circle");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
drawingPanel = new DrawingPanel(drawingPanelSize,
outerCircle);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
animation = new Animation(0);
new Thread(animation).start();
}
The JFrame methods must be called in a certain order. This is the order I use for most of my SWwing applications.
I pack the JFrame. I don't set a JFrame size. I let the Swing layout managers set the size of my JFrame. The default layout of a JFrame content pane is a BorderLayout. I put my drawing JPanel in the center of the BorderLayout.
Next, I create the drawing JPanel.
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private Circle circle;
public DrawingPanel(Dimension size, Circle circle) {
this.circle = circle;
this.setBackground(Color.WHITE);
this.setPreferredSize(size);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Point center = circle.getCenter();
int radius = circle.getRadius();
int diameter = radius + radius;
g2d.setColor(circle.getColor());
g2d.fillOval(center.x - radius, center.y - radius,
diameter, diameter);
}
}
All the drawing JPanel does is paint a Circle object. Pretty straightforward.
The fillOval method paints an oval from the upper left hand corner. We calculate the upper left hand point from the center point.
The responsibility for calculating and updating the outer circle center point falls to my controller class, the Animation class. I use a simple loop to update the theta angle, calculate the new outer circle center point, paint the outer circle, and wait a period of time.
Here's that code.
public class Animation implements Runnable {
private int theta;
public Animation(int theta) {
this.theta = theta;
}
#Override
public void run() {
while (true) {
theta++;
theta = (theta >= 360) ? 0 : theta;
Point center = innerCircle.calculateCircumferencePoint(theta);
outerCircle.setCenter(center);
repaint();
sleep(30L);
}
}
private void repaint() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
drawingPanel.repaint();
}
});
}
private void sleep(long duration) {
try {
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
The Animation repaint method makes the call to the drawing JPanel repaint method inside another SwingUtilities invokeLater method. This method ensures that the drawing happens on the Event Dispatch Thread.
Finally, here's the complete, runnable, example. I used inner classes so I could post the code as one block, and you can copy and run this code as one block. Generally, classes should be in separate files and for a more complex GUI, separate packages.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class RotateCircle implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new RotateCircle());
}
private Animation animation;
private Circle innerCircle;
private Circle outerCircle;
private DrawingPanel drawingPanel;
private Dimension drawingPanelSize;
public RotateCircle() {
this.drawingPanelSize = new Dimension(400, 400);
int innerCircleRadius = drawingPanelSize.width / 4;
int centerX = drawingPanelSize.width / 2;
int centerY = drawingPanelSize.height / 2;
int outerCircleRadius = drawingPanelSize.width / 10;
this.innerCircle = new Circle(innerCircleRadius, null);
this.innerCircle.setCenter(centerX, centerY);
this.outerCircle = new Circle(outerCircleRadius, Color.BLUE);
Point point = innerCircle.calculateCircumferencePoint(0);
this.outerCircle.setCenter(point);
}
#Override
public void run() {
JFrame frame = new JFrame("Rotate Circle");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
drawingPanel = new DrawingPanel(drawingPanelSize,
outerCircle);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
animation = new Animation(0);
new Thread(animation).start();
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private Circle circle;
public DrawingPanel(Dimension size, Circle circle) {
this.circle = circle;
this.setBackground(Color.WHITE);
this.setPreferredSize(size);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Point center = circle.getCenter();
int radius = circle.getRadius();
int diameter = radius + radius;
g2d.setColor(circle.getColor());
g2d.fillOval(center.x - radius, center.y - radius,
diameter, diameter);
}
}
public class Animation implements Runnable {
private int theta;
public Animation(int theta) {
this.theta = theta;
}
#Override
public void run() {
while (true) {
theta++;
theta = (theta >= 360) ? 0 : theta;
Point center = innerCircle.calculateCircumferencePoint(theta);
outerCircle.setCenter(center);
repaint();
sleep(30L);
}
}
private void repaint() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
drawingPanel.repaint();
}
});
}
private void sleep(long duration) {
try {
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Circle {
private final int radius;
private final Color color;
private Point center;
public Circle(int radius, Color color) {
this.radius = radius;
this.color = color;
}
public Point calculateCircumferencePoint(int theta) {
double radians = Math.toRadians(theta);
int x = center.x + (int) Math.round(Math.cos(radians) * radius);
int y = center.y + (int) Math.round(Math.sin(radians) * radius);
return new Point(x, y);
}
public void setCenter(int x, int y) {
this.center = new Point(x, y);
}
public void setCenter(Point center) {
this.center = center;
}
public int getRadius() {
return radius;
}
public Color getColor() {
return color;
}
public Point getCenter() {
return center;
}
}
}
So I've been stuck on this problem for a while now and I'm desperate for help. Please help me. I've got 3 classes:
Circle is just suppose to draw a circle in the frame created by Frame with random starting position (and defind the radius).
Frame is the mainclass with methods such as addCircle(), bounce(), start(), stop(), run() (moves the circles) and quit(). This class also creates the frame in which the circles are added to.
Interfa is just for now a inteface frame where I define the radius, number of circles and Frame size.
No matter what I try I cannot add more than two circle (one is colored and one is not):
The "recursive way":
private static void addCircle(int n){
Circle[] circles = new Circle[n+10];
if (n > 0){
circles[circleAdd] = new Circle();
frame.add(circles[circleAdd]);
circleAdd = circleAdd + 1;
addCircle(n-1);
}
}
Normal itterative way
private static void addCircles(int n){
ArrayList<Circle> circles = new ArrayList<Circle>();
for(int i = 0; i<=n;i++){
circles.add(new Circle());
frame.add(circles.get(i));
}
}
This is how I create my Frame:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public Class Frame{
private static JFrame frame;
private static int circleAdd = 0;
private static JPanel fra;
public static void mainFrame(){
frame = new JFrame();
frame.setSize(500,500);
frame.setVisible(true);
fra = new JPanel();
frame.add(fra);
...
//addCircle and addCircles
...
public static void main..
}
}
This is my circle:
import java.awt.*;
import javax.swing.*;
import java.util.Random;
public class Circle extends JPanel{
private Random random = new Random();
public void paint(Graphics g){
int randX = random.nextInt(250)+50;
int randY = random.nextInt(250)+50;
g.drawOval(randX,randY,50,50);
g.setColor(Color.ORANGE);
g.fillOval(100,100,50,50);
}
}
I would suggest that your general approach is wrong. Instead of using a JPanel as the element, you should have a JPanel capable of painting any number of "circles". The Graphics2D API is capable of drawing complex shapes (including ovals).
The main issues I can see are:
JFrame by default is using a BorderLayout, this only allows a single component to be placed in each of the five available positions
Layout managers rely on the preferred/minimum/maximumSize hints to make determinations about the size of the components. They are also responsible for deciding on where the component should be placed. In your current implementation, this would mean that it's possible for you to paint beyond the visible range of the component
Overriding paint is not recommend, and failing to call super.paint could cause a number of unexpected and difficult to diagnose issues
Painting can occur at any time, so using random values in the paint method will cause the UI to constantly change
Instead, you could define your own Circle class which takes the location and size you want and simply acts as a container
public class Circle {
private int x;
private int y;
private int radius;
private Ellipse2D shape;
public Circle(int x, int y, int radius) {
this.x = x;
this.y = y;
this.radius = radius;
this.shape = new Ellipse2D.Double(x, y, radius * 2, radius * 2);
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getRadius() {
return radius;
}
public Rectangle getBounds() {
return shape.getBounds();
}
public void paint(Graphics2D g2d) {
g2d.setColor(Color.ORANGE);
g2d.fill(shape);
}
}
This is simply a container class, it represents the information need to generate the desired outcome. It has a convince method which is capable of then painting the shape itself.
You would then need to create a List of these shapes and paint them to your component
public class TestPane extends JPanel {
private List<Circle> circles = new ArrayList<>(10);
private Dimension size;
public TestPane() {
Random random = new Random();
int maxX = 0;
int maxY = 0;
for (int index = 0; index < 10; index++) {
int randX = random.nextInt(250) + 50;
int randY = random.nextInt(250) + 50;
circles.add(new Circle(randX, randY, 25));
maxX = Math.max(maxX, randX + 50);
maxY = Math.max(maxY, randY + 50);
}
size = new Dimension(maxX, maxY);
}
#Override
public Dimension getPreferredSize() {
return size;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Circle circle : circles) {
Graphics2D g2d = (Graphics2D) g.create();
circle.paint(g2d);
g2d.dispose();
}
}
}
One of the things you seem to lack understanding in is how painting actually works in Swing.
Start by having a look at Performing Custom Painting and Painting in AWT and Swing for more details.
A deeper understanding of how layout managers and the component hierarchy work also wouldn't hurt
I have inefficient code of a square wave. I have 2 buttons, 1 table and something like a coordinate system where the square appears in. I want the wave to scroll/move in real time until it hits the end of the coordinate system instead of just appearing by selecting both of the buttons. Additionally, if anyone has a better way of drawing a square wave please tell me.
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawLine(20, 300, 20, 450);
g2d.drawLine(20, 350, 400, 350);
g2d.drawLine(20, 400, 400, 400);
g2d.drawLine(20, 450, 400, 450);
if (this.jButtonSTART.isSelected() & this.jButtonAND.isSelected()) {
this.draw(g2d);
}
}
public void draw(Graphics2D g2d) {
boolean up = true;
while (x <= 380) {
g2d.setColor(Color.blue);
if (x > 0 && x % 95 == 0) {
up = !up;
g2d.drawLine(20 + x, up ? 315 : 350 + y, 20 + x, up ? 350 : 315 + y);
} else {
if (up) {
g2d.drawLine(20 + x, 315 + y, 21 + x, y + 315);
} else {
g2d.drawLine(20 + x, 350 + y, 21 + x, y + 350);
}
}
x++;
}
x = 0;
}
Simple way to draw your square wave and move it:
Create a BufferedImage that is longer than your GUI. It should have length that matches a the period of your square wave and be at least twice as long as the GUI component that it's displayed in.
Draw within the paintComponent method override of a JPanel, not the paint method.
Call the super's paintComponent method first within your override.
You'll draw the image using g.drawImage(myImage, imageX, imageY, this) where imageX and imageY are private instance fields of the JPanel-extending drawing class.
In a Swing Timer, advance imageX with each tick of the Timer, that is each time its ActionListener's actionPerformed method is called).
Then call repaint() on the drawing JPanel within the same actionPerformed method.
Done.
for example, note that this code does not do exactly what you're trying to do, but does show an example of Swing animation using a Swing Timer and paintComponent.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class MoveWave extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = 200;
private static final int TIMER_DELAY = 40;
public static final int DELTA_X = 2;
public static final int STARTING_MY_IMAGE_X = -PREF_W;
private static final Color COLOR_1 = Color.RED;
private static final Color COLOR_2 = Color.BLUE;
private static final Color BG = Color.BLACK;
private static final int CIRCLE_COUNT = 10;
private BufferedImage myImage = null;
private int myImageX = STARTING_MY_IMAGE_X;
private int myImageY = 0;
public MoveWave() {
setBackground(BG);
myImage = new BufferedImage(2 * PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = myImage.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(new GradientPaint(0, 0, COLOR_1, 20, 20, COLOR_2, true));
for (int i = 0; i < CIRCLE_COUNT; i++) {
int x = (i * 2 * PREF_W) / CIRCLE_COUNT;
int y = PREF_H / 4;
int width = (2 * PREF_W) / CIRCLE_COUNT;
int height = PREF_H / 2;
g2.fillOval(x, y, width, height);
}
g2.dispose();
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (myImage != null) {
g.drawImage(myImage, myImageX, myImageY, this);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
myImageX += DELTA_X;
if (myImageX >= 0) {
myImageX = STARTING_MY_IMAGE_X;
}
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("MoveWave");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new MoveWave());
frame.setResizable(false);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
I want to create multiple squares that move independently and at the same time,and I think the most efficient way is through the transform method in Graphics2D,but I'm not sure how to make it work for each square.I want the square object to be self contained and create its own transforms(instance transforms). Here's what I have so far.
TransformPanel
import javax.swing.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.util.Random;
import java.util.concurrent.TimeUnit;
public class TranformPanel extends JPanel {
private int[] xcoords = {250,248,253,255,249};
private int[] ycoords = {250,253,249,245,250};
private double randomx = 0;
private double randomy = 0;
public void paintComponent(Graphics g)
{
super.paintComponent(g);
drawTransform(g,randomx,randomy);
}
private void drawTransform(Graphics g,double randomx,double randomy)
{
Random rn = new Random();
int xnum = rn.nextInt(10)-5;
randomx = xnum;
int ynum = rn.nextInt(10)-5;
randomy = ynum;
Rectangle rect = new Rectangle(250,250,10,10);
AffineTransform transform = new AffineTransform();
Graphics2D g2d = (Graphics2D)g;
transform.translate(randomx,randomy);
g2d.draw(transform.createTransformedShape(rect));
}
}
TransformDraw
import java.awt.*;
import javax.swing.*;
import java.awt.geom.AffineTransform;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;
public class TransformDraw{
private static TranformPanel panel = new TranformPanel();
public static void main(String[] args) {
// Setup our JFrame details
JFrame frame = new JFrame();
frame.setTitle("Transform Polygon Example");
frame.setSize(500,500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.setVisible(true);
frame.add(panel);
Scanner input = new Scanner(System.in);
for (int i=0;i<10;i++)
{
try {
TimeUnit.SECONDS.sleep(1);
frame.repaint();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Thanks is Advance!
Start by creating something that can manage it's location (and other properties) and which can be "painted"
public interface Box {
public void update(Dimension size);
public void paint(Graphics2D g2d);
}
So, this is pretty basic, all it can do is be updated (within a given area) and be painted. You could expose other properties (like it's bounding box for example) based on your particular needs
Next, we need a simple implementation, something like...
public class DefaultBox implements Box {
private Color color;
private Rectangle bounds;
private int xDelta;
private int yDelta;
public DefaultBox(Color color, Dimension size) {
this.color = color;
bounds = new Rectangle(new Point(0, 0), size);
xDelta = 1 + (int) (Math.random() * 10);
yDelta = 1 + (int) (Math.random() * 10);
}
#Override
public void update(Dimension size) {
bounds.x += xDelta;
bounds.y += yDelta;
if (bounds.x < 0) {
bounds.x = 0;
xDelta *= -1;
} else if (bounds.x + bounds.width > size.width) {
bounds.x = size.width - bounds.width;
xDelta *= -1;
}
if (bounds.y < 0) {
bounds.y = 0;
yDelta *= -1;
} else if (bounds.y + bounds.height > size.height) {
bounds.y = size.height - bounds.height;
yDelta *= -1;
}
}
#Override
public void paint(Graphics2D g2d) {
g2d.setColor(color);
g2d.fill(bounds);
}
}
Now, this maintains a simple Rectangle instance, which describes the location and size of the object, it also maintains properties about the color and it's speed.
When update is called, it updates it's location and does some simple bounds checking to make sure that the box remains within the specified area.
When paint is called, it simply paints itself.
Finally, we need some way to update and paint these boxes....
public class TestPane extends JPanel {
private List<Box> boxes;
private Color[] colors = {Color.RED, Color.BLACK, Color.BLUE, Color.CYAN, Color.DARK_GRAY, Color.GREEN, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.WHITE, Color.YELLOW};
public TestPane() {
boxes = new ArrayList<>(25);
for (int index = 0; index < 100; index++) {
Color color = colors[(int) (Math.random() * colors.length)];
int width = 10 + (int) (Math.random() * 9);
int height = 10 + (int) (Math.random() * 9);
boxes.add(new DefaultBox(color, new Dimension(width, height)));
}
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Box box : boxes) {
box.update(getSize());
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Box box : boxes) {
Graphics2D g2d = (Graphics2D) g.create();
box.paint(g2d);
g2d.dispose();
}
}
}
Okay, so again, this is pretty simple. It maintains a List of Box's, a Swing Timer to periodically update the List of Box's, calling their update method. The Timer the simply calls repaint, which (in a round about way) ends up calling paintComponent, which then just calls paint on each instance of Box.
100 boxes...
I am trying to create a simple animation which draws random rectangles when a button is pressed. So far I managed to create rectangle on the press of a button. I want to further develop the code so that when I press the button, more than multiple random rectangles are created. I tried to create a for loop which asks the inner class to repaint itself but it still didn't work. can anyone help me please.
public class TwoButtonsRandomRec {
JFrame frame;
private int width = 500;
private int height = 500;
private DrawPanel dp;
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 static void main (String[] args)
{
TwoButtonsRandomRec test = new TwoButtonsRandomRec();
test.go();
}
public void go()
{
dp = new DrawPanel();
JButton start = new JButton("Start");
start.addActionListener(new startListener());
JButton stop = new JButton("Stop");
stop.addActionListener(new stopListener());
frame = new JFrame();
frame.setSize(getWidth(), getHeight());
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(BorderLayout.NORTH, start);
frame.getContentPane().add(BorderLayout.SOUTH, stop);
}
class startListener implements ActionListener{
public void actionPerformed(ActionEvent event){
frame.getContentPane().add(BorderLayout.CENTER, dp);
frame.repaint();
frame.getRootPane().revalidate();
for(int i=0; i<10; i++){
dp.repaint();
}
}
}
class stopListener implements ActionListener{
public void actionPerformed(ActionEvent event){
System.out.println("stop");
}
}
class DrawPanel extends JPanel{
public void paintComponent(Graphics g){
int w = 5+(int)(Math.random() * width-5);
int h = 5+(int)(Math.random() * height-5);
int maxX = width-w; // diffX & diffY are used to ensure that rectangle is
int maxY = width-h; // draw completely inside the window
int x = (int)(Math.random() * maxX);
int y = (int)(Math.random() * maxY);
Color color = new Color((int) (Math.random()*256), // random red
(int) (Math.random()*256), // random green
(int) (Math.random()*256));// random blue
g.setColor(color);
g.fillRect(x,y,w,h);
}
}
}
repaint() simply tells Swing "when you'll have time, please repaint this area". So if you add rectangles in a loop and call repaint at each iteration, all the rectangles will only appear after the loop has finished, and the action event has been handled.
To have an animation, you need to loop in a separate thread. The easiest way to do that is to use a Swing Timer. When the Start button is started, start a timer which adds a random rectangle and calls repaint() every X milliseconds. When the Stop button is pressed, stop the timer.
What you should do is to put the loop inside paintComponent method and not call repaint in the loop.
So your paintComponent method should look like this:
public void paintComponent(Graphics g){
for (int i = 0; i < 10; i++) {
int w = 5+(int)(Math.random() * width-5);
int h = 5+(int)(Math.random() * height-5);
int maxX = width-w; // diffX & diffY are used to ensure that rectangle is
int maxY = width-h; // draw completely inside the window
int x = (int)(Math.random() * maxX);
int y = (int)(Math.random() * maxY);
Color color = new Color((int) (Math.random()*256), // random red
(int) (Math.random()*256), // random green
(int) (Math.random()*256));// random blue
g.setColor(color);
g.fillRect(x,y,w,h);
}
}
And your action performed should look like this:
public void actionPerformed(ActionEvent event){
frame.getContentPane().add(BorderLayout.CENTER, dp);
frame.repaint();
frame.getRootPane().revalidate();
dp.repaint();
}
Well,here I have done a short EG for you.It displays random rectangles, random times on random screen location.
(You can set your own value of randomization, and the screen location max bound,as per your requirements.)
And also note
int i=(int)(Math.random()*10);
int j=(int)(Math.random()*10);
for(;i<j;i++)
Where at times i may be > than j.So,loop may not work on one or two cliks.Change as per your need.
Here is the working code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class SimpleStamper extends JApplet {
public void init() {
Display display = new Display();
setContentPane(display);
}
class Display extends JPanel implements MouseListener {
Display() {
setBackground(Color.black);
addMouseListener(this);
}
public void mousePressed(MouseEvent evt) {
if ( evt.isShiftDown() ) {
repaint();
return;
}
int x = evt.getX();
int y = evt.getY();
Graphics g = getGraphics();
//***MODIFY THE FOLLOWING LINES****//
int i=(int)(Math.random()*10);
int j=(int)(Math.random()*10);
for(;i<j;i++)
{ g.setColor(Color.red);
x=(int)(Math.random()*100);
y=(int)(Math.random()*100);
g.fillRect( x , y , 60, 30 );
g.setColor(Color.black);
g.drawRect(x , y , 60, 30 );}
g.dispose();
}
public void mouseEntered(MouseEvent evt) { }
public void mouseExited(MouseEvent evt) { }
public void mouseClicked(MouseEvent evt) { }
public void mouseReleased(MouseEvent evt) { }
}
}