Creating an Animated 4X4 Grid in Java

Animation sequence in JFrame

Alright, there are a lot of problems in your code, let's step into each of them:

  1. You have a lot of spaces between lines, that makes your code a lot larger and harder to read

  2. You haven't indented your code correctly (see the last } on your code, it's at the same level than the others; your for loops, etc), it makes the code so much harder to read and understand as well

  3. You're creating a JFrame but extending JLabel, I'm not sure why you're doing this, if you're doing it so you can use the paintComponent() method, it's not necessary, on my code you can see how you can do it w/o extending any Component

  4. If you haven't read the Swing Timer docs you should click that link and read what the ActionListener parameter does. In this case, we're going to use this listener to call the repaint() method and update our currentImage (or nextImage in the code below) and change the image accordingly. You failed to do this.

  5. You were creating more than 1 Timer too, you created 6 here! All of them new but they had no action to do when the time finished

    for (int i=0; i < nekopics.length; i++) {
    timer = new Timer(1000, this);
    timer.setInitialDelay(0);
    timer.start();
    currentimg = nekopics[i];
    repaint();
    }
  6. You're changing unnecessarily the visibility of the paintComponent() method to public from protected


However I want to congratulate you for not using a null layout and following the recommendations I made on the comments above.


And finally the code that changes one image for another inside a Timer is the following, you can copy-paste it and change the image's names so you can see how it works.

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileInputStream;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class ImageSequence {
private JFrame frame;
private JPanel pane;
private Timer timer;
private int nextImage = 0;
private String[] images = {"tokyo", "tokyo2", "starwars"};
private Image img = null;
public static void main (String args[]) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new ImageSequence().createAndShowGui();
}
});
}

public void createAndShowGui() {
frame = new JFrame("Image Sequence");
timer = new Timer(1000, listener);
pane = new JPanel() {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
try {
img = ImageIO.read(new FileInputStream("/home/jesus/Pictures/" + images[nextImage] + ".jpg"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
g.drawImage(img , 0, 0, 200, 200, this);
}

@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
};

timer.start();

frame.getContentPane().add(pane);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}

ActionListener listener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
System.out.println(nextImage);
nextImage = nextImage < images.length - 1 ? nextImage + 1 : 0;
System.out.println(nextImage);
pane.repaint();
}
};
}

Animated Sprites with Java Swing

  1. First of all you have a lot of white spaces between each line, it makes it hard to read the code.

  2. Yes, you could try using a Swing Timer, here is an example and another example and another example.

  3. You have an empty catch block which is not secure, at least do this:

    catch (IOException e){
    e.printStackTrace();
    }
  4. You're not placing your program on the Event Dispatch Thread (EDT) to solve it just change your main method as follows:

    public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
    @Override
    public void run() {
    //Your constructor here
    }
    });
    }
  5. You're extending JFrame but not making use of the frame generated by it, and at the same time you're creating an instance of a JFrame, remove the extends JFrame in your code. Related reading: Java Swing using extends JFrame vs calling it inside of class

  6. Instead of calling frm1.setSize(400, 400); override the Painel1's getPreferredSize() method to return a new Dimension of 400, 400 and call frm1.pack()

    @Override
    public Dimension getPreferredSize() {
    return new Dimension(400, 400);
    }
  7. The animation processing is too fast!

    It's not the animation processing too fast, but the for loop prevents the GUI to be painted before it ends, that's why you're only seeing the last sprite being painted.


With all the above points in mind, you now can have your code as follows, which includes the use of a Swing Timer and the above recommendations already included:

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.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class Sprites {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {

@Override
public void run() {
JFrame frm1 = new JFrame();
Painel1 pn1 = new Painel1();
frm1.getContentPane().add(pn1);

frm1.pack();
frm1.setVisible(true);
frm1.setLocationRelativeTo(null);
frm1.setResizable(false);
frm1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
});
}
}

class Painel1 extends JPanel {

int[][] spriteSheetCoords = { { 8, 10, 119, 129 }, { 138, 10, 118, 130 }, { 267, 10, 118, 132 },
{ 402, 11, 113, 132 }, { 538, 12, 106, 134 }, { 671, 13, 103, 133 }, { 804, 12, 102, 132 },
{ 23, 161, 100, 134 }, { 157, 162, 96, 134 }, { 287, 159, 95, 135 }, { 418, 158, 95, 133 },
{ 545, 159, 99, 133 }, { 673, 159, 102, 134 }, { 798, 158, 108, 130 }, { 9, 309, 116, 126 },
{ 137, 309, 118, 127 }, { 274, 310, 110, 128 }, { 412, 311, 102, 129 }, { 541, 312, 103, 130 },
{ 671, 312, 104, 131 }, { 806, 312, 98, 132 }, { 29, 463, 94, 135 }, { 155, 462, 98, 135 },
{ 279, 461, 104, 135 }, { 409, 461, 106, 135 }, { 536, 461, 109, 135 }, { 662, 461, 112, 133 } };

int i = 0;
BufferedImage img;

private ActionListener actionListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
i++;
if (i == spriteSheetCoords.length) {
i = 0;
}
revalidate();
repaint();
}
};

public Painel1() {
Timer timer = new Timer(50, actionListener);
timer.setInitialDelay(0);
timer.start();
setBackground(Color.yellow);
try {
img = ImageIO.read(new File("/home/jesus/Pictures/tokyo.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
}

@Override
public void paintComponent(Graphics g) {
Image subSprite;
super.paintComponent(g);
subSprite = img.getSubimage(spriteSheetCoords[i][0], spriteSheetCoords[i][1], spriteSheetCoords[i][2], spriteSheetCoords[i][3]);
g.drawImage(subSprite, 140, 120, null);
}

@Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}

Sample Image

As you can see the Timer has a delay of 50ms to make the transition of the sprites smoother, you can adjust it as you please.

visualizing JPanel change within a loop

Don't use a loop. Swing will only repaint the frame once the entire loop has finished executing.

Instead you need to use a Swing Timer. When the Timer fires you invoke your logic. Read the section from the Swing tutorial on How to Use Swing Timers.

Here is a simple example of a Timer that simply displays the time every second: Update a Label with a Swing Timer

Also, don't remove/add panels. Instead you can use a Card Layout and sway the visible panel. Again read the tutorial on How to Use CardLayout.

How do you display a looping statement in a text box?

You should build the full text in a StringBuilder:

int num = Integer.parseInt(userText.getText());
StringBuilder buf = new StringBuilder();
for (i = 1; i <= num; i++) {
if (num % i == 0)
buf.append(" ").append(i);
}
textFieldAns.setText(buf.substring(1));

Replacing an image once through timer

You're never initializing timerListener

private ActionListener timerListener;

Inside your constructor you have to call (With Java 8 lambdas):

timerListener = e -> {
react = new ImageIcon("reactCircle.png");
main.setIcon(react);
}

Or (Java 7 and lower):

timerListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
react = new ImageIcon("reactCircle.png");
main.setIcon(react);
}
}

And don't forget to call timerListener.stop() once the timer has fired, so that you don't keep computing more times

From Andrew Thompson's comment below:

As you only want to replace the image once, call timerListener.setRepeats(false) on your constructor. Check the docs for more information about it.

How to make a 4x4 sudoku solver in java

Here's a page that explains this with a 4x4 grid. By the way, you really should try Googling before asking a question here. I just searched "sudoku solver example program" to find this.

http://www.mathworks.com/company/newsletters/articles/solving-sudoku-with-matlab.html

Creating a Countdown Timer inside of a JFrame

Here's another way to do it. Please note this code is not test.

private void initialize() {
...
new Thread() {
int counter = 10;
public void run() {
while(counter >= 0) {
lblNewLabel.setText("Test" + (counter--));
try{
Thread.sleep(1000);
} catch(Exception e) {}
}
}
}.start();
}


Related Topics



Leave a reply



Submit