I have one dilemma , how to realize application. I have JPanel with width 288 and height 512, then I created two objects ( images ) and drew them through paintComponent using coordinates
drawImage (Image1,288,128,this) ;
drawImage (Image2, 288, 384, this);
. They are decrementing simultaneously in the X axis and when it reaches x = 144 , new (same) images should be drawn at the coordinates ‘( x = 288 , y = (int)Math.random()* 512 )’ and begin decrement as well as first ones should still decrements. And this process should be endless. Every new objects reaching x = 144 should build new ones . I tried to create ArrayList with adding coordinates in it
ArrayList arrayX = new ArrayList();
arrayX.add(288)
arrayY.add((int) Math.random()* 512 )
and then extract values through
array.get()
But that was unsuccessfully.
I saw video where man did it using JavaScript through the array
var position = []
position = ({
X : 288
Y : 256
})
And then implemented through the loop like this
function draw() {
for (int i = 0; i < position.length; i++ ){
cvs.drawImage(Image1,position[i].x , position[i].y)
cvs.drawImage(Image2,position[i].x , position[i].y + 50)
position [i] .x - -;
if(position[i].x == 128)
position.push({
X : 288
Y : Math.floor(Math.random()*512 })
})
}
}
I don’t know how to do this in Java.
May be I should use array too to keep variables with coordinates , or arraylist but in different way. Help me please .
Thanks in advance
My answer is completely based on MadProgrammer's answer (A comprehensive tutorial actually).
From what I read in the post : "Every new objects reaching x = 144 should build new ones", I think the desired implementation is slightly different:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class ImageAnimator {
public ImageAnimator() {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new AnimationPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
public static class Drawable {
private int x;
private final int y;
private static final Image image = image();
//construct with a random y value
public Drawable(int x) {
this(x, -1);
}
public Drawable(int x, int y) {
this.x = x;
this.y = y < 0 ? (int) (Math.random() * (512 - 20)) : y;
}
public int getX() { return x; }
public int getY() { return y; }
public void update() { x--; }
public Image getImage(){ return image; }
public static Image image() {
URL url = null;
try {
url = new URL("https://dl1.cbsistatic.com/i/r/2017/09/24/b2320b25-27f3-4059-938c-9ee4d4e5cadf/thumbnail/32x32/707de8365496c85e90c975cec8278ff5/iconimg241979.png");
return ImageIO.read(url);
} catch ( IOException ex) {
ex.printStackTrace();
return null;
}
}
}
public class AnimationPane extends JPanel {
private final List<Drawable> drawables;
private static final int W = 288, H = 512, CYCLE_TIME = 5;
public AnimationPane() {
drawables = new ArrayList<>(2);
drawables.add(new Drawable(W, H/4));
drawables.add(new Drawable(W, 3*H/4));
Timer timer = new Timer(CYCLE_TIME, e -> animate());
timer.start();
}
private void animate() {
for (Drawable drawable : new ArrayList<>(drawables)) {
drawable.update();
if(drawable.getX() == W/2) {
drawables.add(new Drawable(W)); //random Y
}
if(drawable.getX() <= 0) {
drawables.remove(drawable);
}
}
repaint();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(W, H);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Drawable drawable : drawables ) {
g.drawImage(drawable.getImage(),drawable.getX(), drawable.getY(), null);
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(()->new ImageAnimator());
}
}
Conceptually the idea is simple enough, the problem is, Swing is signal thread and NOT thread safe.
See Concurrency in Swing for more details.
This means you can run a long running or blocking operation (like a never ending loop) inside the Event Dispatching Thread, but also, you shouldn't update the UI (or properties the UI depends on) from outside the context of the EDT.
While there are a number of possible solutions to the problem, the simplest is probably to use a Swing Timer
, which provides a means to schedule a delay safely (that won't block the EDT) and which will trigger it's updates within the context of the EDT, allowing you to update the UI from within it.
See How to Use Swing Timers for more details.
Now, because you're in a OO language, you should leverage the power it provides, to me, this means encapsulation.
You have a image, you want drawn at a specific location, but whose location change be changed based on some rules, this just screams Plain Old Java Old (POJO)
Normally, I'd start with a interface
to describe the basic properties and operations, but for brevity, I've jumped straight for a class...
public class Drawable {
private int x, y;
private Color color;
public Drawable(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Color getColor() {
return color;
}
public void update() {
x--;
if (x <= 144) {
reset();
}
}
protected void reset() {
x = 288;
y = (int) (Math.random() * (512 - 20));
}
public void paint(Graphics2D g2d) {
Graphics2D copy = (Graphics2D) g2d.create();
copy.translate(getX(), getY());
copy.setColor(getColor());
copy.drawOval(0, 0, 20, 20);
copy.dispose();
}
}
But wait, you say, it's using Color
instead of image!? Yes, I didn't have any small images at hand, besides, I need to leave you something to do ;)
Now, the animation is a sequence of updating and painting repeatedly until a desired state is reached.
In this case, you don't care about the end state so much, so you can just keep it running.
The "update" cycle is handled by a Swing Timer
, which loops over a List
of Drawable
objects, calls their update
methods and then schedules a repaint
, which triggers the JPanel
s paintComponent
where by the Drawable
objects are painted, simple