I'd like to create a fluent 60fps animation. The animation itself seems to be approximately at 10fps, even though my render thread calls the run method 60 times a second. I'm pretty sure the problem is that the Swing was not created with animations in mind. So what is the easiest way to solve this? Thanks a lot
Code:
import aurelienribon.tweenengine.Tween;
import aurelienribon.tweenengine.TweenManager;
import object.Text;
import java.awt.*;
public class Main implements RenderThread.RunnerIface {
private final Frame mFrame;
private RenderThread mRenderThread;
private TweenManager mTweenManager;
private Text mText = new Text("text", 250, 250);
public Main() {
mFrame = new Frame();
mRenderThread = new RenderThread(this);
mTweenManager = new TweenManager();
Tween.registerAccessor(Text.class, new TextAccessor());
new Thread(mRenderThread).start();
this.animateRight();
}
private void animateLeft() {
Tween.to(mText, -1, 1000.0f)
.target(100, 250)
.start(mTweenManager);
}
private void animateRight() {
Tween.to(mText, -1, 10000.0f)
.target(400, 250)
.start(mTweenManager);
}
public static void main(String[] args) {
new Main();
}
#Override
public void run(float delta) {
mTweenManager.update(delta);
System.out.format("Delta: %f, FPS: %f\n", delta, 1000/delta);
Graphics g = mFrame.getCanvas().getGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, mFrame.getCanvas().getWidth(), mFrame.getCanvas().getHeight());
mText.render(g);
}
}
Edit:
Second attempt:
public class DrawPanel extends JPanel implements ActionListener {
private Timer tm = new Timer(30, this);
JLabel label = new JLabel("Here is my label");
private int x = 0, velX = 2;
private long time;
public DrawPanel() {
super();
// this.setLayout(null);
this.x = this.getWidth();
this.label.setBounds(x,100,100,100);
this.add(label);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
/* g.setColor(Color.white);
g.fillRect(0, 0, this.getWidth(), this.getHeight());*/
// x=this.getWidth();
time = System.currentTimeMillis();
tm.start();
}
#Override
public void actionPerformed(ActionEvent e) {
// x+=1;
int l = (int) ((System.currentTimeMillis() - time)*0.1);
this.time = System.currentTimeMillis();
System.out.println(l);
x=x+l;
this.label.setBounds(x,100,100,100);
this.repaint();
}
}
public class Main {
private Frame mFrame;
private DrawPanel drawPanel;
public Main() {
mFrame = new Frame();
drawPanel = new DrawPanel();
mFrame.add(drawPanel);
}
public static void main(String[] args) {
new Main();
}
}
So I completely rewrote my code following MadProgrammer's advices. Now I use swing timer instead of my render thread. Result is still the same, the animation is dropping frames and it's unusable. I'm confident that the actionPerformed method is called as frequently as it should be. I guess the problem is somehow hidden in the repaint method. I tried to call the repaint method on the JFrame but it didn't help.
I was following this tutorial, but everythink seems to work fine in the creator's case.
Thanks
Due to the performance problems with Swing I decided to use JavaFX instead. Since I've created quite a lot of code in Swing I didn't want to toss it all away. Fortunately there is JFXPanel for that.
Here is the code with animation example:
public class AnimationPanel extends JFXPanel {
private Text[] nodes = new Text[1];
public AnimationPanel() {
super();
Text t = new Text(10,50, "This is test");
t.setFill(Color.RED);
nodes[0] = t;
final Scene scene = new Scene(new Group(nodes), 800, 600, Color.BLACK);
this.setScene(scene);
new AnimationTimer() {
public int x = 0;
#Override
public void handle(long now) {
for (int i=0; i<1; i++) {
final Node node = nodes[i];
node.setTranslateX(x);
x++;
}
}
}.start();
}
}
You can use this panel as a regular Swing panel.
Related
I've written some code that essentially animates a sequence of images and adds them to a frame when I run the file.
I want to implement a functionality where I can add this animation to two different areas of a JPanel that has a BorderLayout (North and West).
I want to do this using a button but I don't know how to do that. I am new to event handling and layout managers.
How do I go about this?
My code for the animation:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ImageSequence extends JPanel implements ActionListener {
private ImageSQPanel imageSQPanel;
private static int frameNumber = -1;
private Timer timer;
private void buildUI(Container container, Image[] arrows) {
int fps = 10;
int delay = 1000 / fps;
timer = new Timer(delay, this);
timer.setInitialDelay(0);
timer.setCoalesce(true);
imageSQPanel = new ImageSQPanel(arrows);
container.add(imageSQPanel, BorderLayout.CENTER);
}
private synchronized void startAnimation() {
if (!timer.isRunning()) {
timer.start();
}
}
public void actionPerformed(ActionEvent e) {
frameNumber++;
imageSQPanel.repaint();
}
class ImageSQPanel extends JPanel {
Image arrowAnimation[];
ImageSQPanel(Image[] arrowAnimation) {
this.arrowAnimation = arrowAnimation;
}
//Draw the current frame of animation.
public void paintComponent(Graphics g) {
super.paintComponent(g); //paint background
//Paint the frame into the image.
try {
g.drawImage(arrowAnimation[ImageSequence.frameNumber % 10], 0, 0, this);
} catch (ArrayIndexOutOfBoundsException e) {
//On rare occasions, this method can be called
//when frameNumber is still -1. Do nothing.
}
}
}
//Invoked only when this is run as an application.
public static void main(String[] args) {
Image[] waving = new Image[7];
for (int i = 1; i <= 7; i++) {
waving[i - 1] = Toolkit.getDefaultToolkit().getImage(
"/Users/sarthaksachdeva/Documents/IntelliJ Projects/Animation/src/images/Arrow" + i + ".png");
}
JFrame f = new JFrame("ImageSequenceTimer");
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
ImageSequence controller = new ImageSequence();
controller.buildUI(f.getContentPane(), waving);
controller.startAnimation();
f.setSize(new Dimension(200, 200));
f.setVisible(true);
}
}
Im trying to create a game in java.
At the first I've made a Jpanel and made a FPS counter and the background was white.
after that I've tried to made that when you pressing ESCAPE, the Jpanel shutting down and then I've tried to make that when you pressing Space, the Background moving to green but after that, when Im trying to start, the Jpanel moving to grey background and the FPS counter has been disappear.
this is the frame class
public class Frame extends JFrame{
public static void main(String[] args) {
new Frame();
}
public Frame(){
new JFrame();
this.setTitle("Tower Defence - By Sahar Haine");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setExtendedState(MAXIMIZED_BOTH);
this.setUndecorated(true);
this.setResizable(false);
this.setVisible(true);
Screen screen = new Screen(this);
this.add(screen);
}
this is the screen class:
public class Screen extends JPanel implements Runnable{
Thread thread = new Thread(this);
Frame frame;
private int fps = 0;
public int scene;
public Boolean running = false;
public Screen(Frame frame){
this.frame = frame;
this.frame.addKeyListener(new KeyHandller(this));
thread.start();
}
#Override
public void paintComponent(Graphics g){
g.clearRect(0, 0, this.frame.getWidth(), this.frame.getHeight());
g.drawString(fps + "", 10, 10);
if(scene == 0){
g.setColor(Color.BLUE);
}else{
}
g.setColor(Color.GREEN);
g.fillRect(0, 0, this.frame.getWidth(), this.frame.getHeight());
}
public void run() {
System.out.println("Success");
long lastFrame = System.currentTimeMillis();
int frames = 0;
running = true;
scene = 0;
while(running){
try {
repaint();
frames++;
if(System.currentTimeMillis() - 1000>= lastFrame){
fps = frames;
frames = 0;
lastFrame = System.currentTimeMillis();
}
Thread.sleep(1);
} catch (InterruptedException ex) {
Logger.getLogger(Screen.class.getName()).log(Level.SEVERE, null, ex);
}
}
System.exit(0);
}
public class KeyTyped{
public void keyESC(){
running = false;
}
public void keySPACE() {
scene = 1;
}
and this is the key handler class:
public class KeyHandler implements KeyListener{
private Screen screen;
private Screen.KeyTyped keyTyped;
public KeyHandler(Screen screen){
this.screen = screen;
this.keyTyped = this.screen.new KeyTyped();
}
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
System.out.println(e.getKeyCode());
if(keyCode == 27){
this.keyTyped.keyESC();
}
if(keyCode == 27){
this.keyTyped.keySPACE();
}
}
public void keyReleased(KeyEvent e) {
}
One issue that jumps out immediately is that you have hard coded "keyCode = 27" for both Escape and Space which is not correct. "27 - Escape" and "32 - Space". Rather than hard code the values, please use KeyEvent.VK_ESCAPE and KeyEvent.VK_SPACE as documented here, KeyEvents
EDIT 1
Also make certain that you draw the string AFTER the "Fill" or else the "Fill" will over paint the FPS string and you will not see it.
I think you have to call
super.paintComponent(g)
as the first line in your paintComponent method override.
I want to have transparent panels in my GUI (if like Windows 7 window headers, it is better).
Before I have used com.sun.awt.AWTUtilities as
AWTUtilities.setWindowOpacity(frame, (float)0.90);
but its parameter is a window like JFrame and couldn't be used for JPanel.
Also I want to have effects on JPanel or JLabel for example luminescence, as is on Windows 7 header buttons. Any other interesting effect is also helpful for me.
Please see the tutorials How to Create Translucent and Shaped Windows and* How to Create Translucent and Shaped Windows*. Follow the links to excellent example depots by #camickr.
For example,
import java.awt.event.*;
import java.awt.Color;
import java.awt.AlphaComposite;
import javax.swing.*;
import javax.swing.UIManager.LookAndFeelInfo;
public class ButtonTest {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ButtonTest().createAndShowGUI();
}
});
}
private JFrame frame;
private JButton opaqueButton1;
private JButton opaqueButton2;
private SoftJButton softButton1;
private SoftJButton softButton2;
public void createAndShowGUI() {
opaqueButton1 = new JButton("Opaque Button");
opaqueButton2 = new JButton("Opaque Button");
softButton1 = new SoftJButton("Transparent Button");
softButton2 = new SoftJButton("Transparent Button");
opaqueButton1.setBackground(Color.GREEN);
softButton1.setBackground(Color.GREEN);
frame = new JFrame();
frame.getContentPane().setLayout(new java.awt.GridLayout(2, 2, 10, 10));
frame.add(opaqueButton1);
frame.add(softButton1);
frame.add(opaqueButton2);
frame.add(softButton2);
frame.setSize(567, 350);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
Timer alphaChanger = new Timer(30, new ActionListener() {
private float incrementer = -.03f;
#Override
public void actionPerformed(ActionEvent e) {
float newAlpha = softButton1.getAlpha() + incrementer;
if (newAlpha < 0) {
newAlpha = 0;
incrementer = -incrementer;
} else if (newAlpha > 1f) {
newAlpha = 1f;
incrementer = -incrementer;
}
softButton1.setAlpha(newAlpha);
softButton2.setAlpha(newAlpha);
}
});
alphaChanger.start();
Timer uiChanger = new Timer(3500, new ActionListener() {
private LookAndFeelInfo[] laf = UIManager.getInstalledLookAndFeels();
private int index = 1;
#Override
public void actionPerformed(ActionEvent e) {
try {
UIManager.setLookAndFeel(laf[index].getClassName());
SwingUtilities.updateComponentTreeUI(frame);
} catch (Exception exc) {
exc.printStackTrace();
}
index = (index + 1) % laf.length;
}
});
uiChanger.start();
}
public static class SoftJButton extends JButton {
private static final JButton lafDeterminer = new JButton();
private static final long serialVersionUID = 1L;
private boolean rectangularLAF;
private float alpha = 1f;
public SoftJButton() {
this(null, null);
}
public SoftJButton(String text) {
this(text, null);
}
public SoftJButton(String text, Icon icon) {
super(text, icon);
setOpaque(false);
setFocusPainted(false);
}
public float getAlpha() {
return alpha;
}
public void setAlpha(float alpha) {
this.alpha = alpha;
repaint();
}
#Override
public void paintComponent(java.awt.Graphics g) {
java.awt.Graphics2D g2 = (java.awt.Graphics2D) g;
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
if (rectangularLAF && isBackgroundSet()) {
Color c = getBackground();
g2.setColor(c);
g.fillRect(0, 0, getWidth(), getHeight());
}
super.paintComponent(g2);
}
#Override
public void updateUI() {
super.updateUI();
lafDeterminer.updateUI();
rectangularLAF = lafDeterminer.isOpaque();
}
}
}
If you have time I recommend you go through this Filty Rich Clients. By using this book you can learn to create stunning visual and animated effects with Swing and Java 2D. Learn graphics and animation fundamentals as well as advanced rendering techniques.
EDIT:
To creat transparent panels call
setOpaque(false)
It'll pass off painting the background to its parent, which may draw its own background.
You can do a screen capture and then use that to paint the background of the panel.
I have the following program which has some very strange and unwanted behavior when it runs. Its supposed to have two buttons, "Start" and "Stop, but when I click "Start" another button shows up right below "Start". Here's a print screen of what I'm talking about:
What am I doing wrong and how do I fix this ugly problem?
Here's the code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
public class TwoButtonsTest {
JFrame frame;
Timer timer;
boolean isClicked;
public static void main(String[] args) {
TwoButtonsTest test = new TwoButtonsTest();
test.go();
}
public void go() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
JButton startButton = new JButton("Start");
startButton.addActionListener(new StartListener());
JButton stopButton = new JButton("Stop");
stopButton.addActionListener(new StopListener());
final DrawPanel myDraw = new DrawPanel();
frame.getContentPane().add(BorderLayout.CENTER, myDraw);
frame.getContentPane().add(BorderLayout.NORTH, startButton);
frame.getContentPane().add(BorderLayout.SOUTH, stopButton);
frame.setVisible(true);
timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
myDraw.repaint();
}
});
}
class StartListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
//needs to be implemented
if(!isClicked) {
}
isClicked = true;
timer.start();
}
}
class StopListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
//needs to be implemented
timer.stop();
isClicked = false;
}
}
class DrawPanel extends JPanel {
public void paintComponent(Graphics g) {
int red = (int)(Math.random()*256);
int blue = (int)(Math.random()*256);
int green = (int)(Math.random()*256);
g.setColor(new Color(red, blue, green));
Random rand = new Random();
// following 4 lines make sure the rect stays within the frame
int ht = rand.nextInt(getHeight());
int wd = rand.nextInt(getWidth());
int x = rand.nextInt(getWidth()-wd);
int y = rand.nextInt(getHeight()-ht);
g.fillRect(x,y,wd,ht);
}
} // close inner class
}
Also I'm trying to get the Start button to do two things. One is to of course start the animation but when the Stop button is pressed and I press Start again, I want it to clean the screen so to speak and start the animation again a new. Any tips on that?
You do not call super.paintComponent(Graphics g) in overriden paintComponent(..) method which you should in order to honor the paint chain and thus the painting of other components.
This call should also be the first call within the method:
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
//do painting here
}
A probem might arise that drawings are not persistent. You must than have a way to store drawings and redraw every time. The most common is an ArrayList which will hold objects to be drawn (thus you cann add to the list remove etc), you would than iterate over the list and redraw each object in paintComponent. See my answer here for an example.
Also please remember to create and manipulate Swing components on Event Dispatch Thread :
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//create UI and components here
}
});
Dont call setSize(..) on JFrame rather override getPreferredSize() of JPanel and return an appropriate height which fits all components, than call JFrame#pack() before setting JFrame visible (but after adding all components).
No need for getContentPane().add(..) as of Java 6+ add(..) defaults to contentPane
Do not re declare Random i.e Random r=new Random() each time paintComponent is called as this will make the distributions of the values less random rather initiate it once when class is created and call methods on the instance
Here is the fixed code (with above fixes implemented):
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.*;
public class TwoButtonsTest {
JFrame frame;
Timer timer;
boolean isClicked;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
TwoButtonsTest test = new TwoButtonsTest();
test.go();
}
});
}
final DrawPanel myDraw = new DrawPanel();
public void go() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton startButton = new JButton("Start");
startButton.addActionListener(new StartListener());
JButton stopButton = new JButton("Stop");
stopButton.addActionListener(new StopListener());
frame.add(myDraw, BorderLayout.CENTER);
frame.add(startButton, BorderLayout.NORTH);
frame.add(stopButton, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
myDraw.repaint();
}
});
}
class StartListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
//needs to be implemented
if (!isClicked) {
}
myDraw.clearRects();
isClicked = true;
timer.start();
}
}
class StopListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
//needs to be implemented
timer.stop();
isClicked = false;
}
}
class DrawPanel extends JPanel {
private ArrayList<MyRectangle> rects = new ArrayList<>();
private Random rand = new Random();
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
addRect();
for (MyRectangle r : rects) {
g.setColor(r.getColor());
g.fillRect(r.x, r.y, r.width, r.height);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
public void clearRects() {
rects.clear();
}
public void addRect() {
// following 4 lines make sure the rect stays within the frame
int ht = rand.nextInt(getHeight());
int wd = rand.nextInt(getWidth());
int x = rand.nextInt(getWidth() - wd);
int y = rand.nextInt(getHeight() - ht);
int red = (int) (Math.random() * 256);
int blue = (int) (Math.random() * 256);
int green = (int) (Math.random() * 256);
rects.add(new MyRectangle(x, y, wd, ht, new Color(red, blue, green)));
}
} // close inner class
}
class MyRectangle extends Rectangle {
Color color;
public MyRectangle(int x, int y, int w, int h, Color c) {
super(x, y, w, h);
this.color = c;
}
public Color getColor() {
return color;
}
}
I wish I could offer a solution, but as of yet I haven't found one. I can tell you the root of the "problem" here lies in the way you are drawing the Center section of your BorderLayout. You are overriding the whole paintComponent() function for this program and having whatever it creates put into the Center of your BoarderLayout. In this case, each time you click a button, the program calls the repaint to draw the image of a clicked button, but since you have also added ANY of the drawn objects to the Center panel, it also is drawn there. Since this specific repaint doesn't specify a location, it goes in the upper left corner.
I fixed your button problem on my Windows XP computer by invoking SwingUtilities.
I formatted your Java code.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class TwoButtonsTest implements Runnable {
JFrame frame;
Timer timer;
boolean isClicked;
public static void main(String[] args) {
SwingUtilities.invokeLater(new TwoButtonsTest());
}
#Override
public void run() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
JButton startButton = new JButton("Start");
startButton.addActionListener(new StartListener());
JButton stopButton = new JButton("Stop");
stopButton.addActionListener(new StopListener());
final DrawPanel myDraw = new DrawPanel();
frame.getContentPane().add(BorderLayout.CENTER, myDraw);
frame.getContentPane().add(BorderLayout.NORTH, startButton);
frame.getContentPane().add(BorderLayout.SOUTH, stopButton);
frame.setVisible(true);
timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
myDraw.repaint();
}
});
}
class StartListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
// needs to be implemented
if (!isClicked) {
}
isClicked = true;
timer.start();
}
}
class StopListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
// needs to be implemented
timer.stop();
isClicked = false;
}
}
class DrawPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
int red = (int) (Math.random() * 256);
int blue = (int) (Math.random() * 256);
int green = (int) (Math.random() * 256);
g.setColor(new Color(red, blue, green));
Random rand = new Random();
// following 4 lines make sure the rect stays within the frame
int ht = rand.nextInt(getHeight());
int wd = rand.nextInt(getWidth());
int x = rand.nextInt(getWidth() - wd);
int y = rand.nextInt(getHeight() - ht);
g.fillRect(x, y, wd, ht);
}
} // close inner class
}
To clean the screen when you press the Start button, you're going to have to add some methods to your DrawPanel class.
Here's one way to do it.
class DrawPanel extends JPanel {
protected boolean eraseCanvas;
public void setEraseCanvas(boolean eraseCanvas) {
this.eraseCanvas = eraseCanvas;
}
#Override
public void paintComponent(Graphics g) {
if (eraseCanvas) {
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
} else {
int red = (int) (Math.random() * 256);
int blue = (int) (Math.random() * 256);
int green = (int) (Math.random() * 256);
g.setColor(new Color(red, blue, green));
Random rand = new Random();
// following 4 lines make sure the rect stays within the frame
int ht = rand.nextInt(getHeight());
int wd = rand.nextInt(getWidth());
int x = rand.nextInt(getWidth() - wd);
int y = rand.nextInt(getHeight() - ht);
g.fillRect(x, y, wd, ht);
}
}
} // close inner class
im doing a final project for school, but my code is doing something really weird..
if i call a function within the class, the jpanel is displayed, but when i call this function from another class, it doesnt..
I found a solution..
calling a method repaint (); after creating 2jpanels.
solve it.
now... how can I close this question, or mark it as solved??
thx alot to everyone who read this.
here is the class.
main class
public class Main {
public static void main(String[] args) {
new GameFrame();}
}
GameFrame class
public class GameFrame extends JFrame {
private static final long serialVersionUID = 1L;
private static final int baseWidth=192,baseHeight=352;
private JPanel panel;
private GamePanel gamePanel;
public GameFrame() {
panel = (JPanel) this.getContentPane();
Dimension d = new Dimension(baseWidth, baseHeight);
panel.setPreferredSize(new Dimension(d));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
this.setTitle("test game..");
this.setResizable(false);
this.setLocationRelativeTo(null);
this.setVisible(true);
init_panelGame();//this will content the real game panel.
}
private void init_panelGame() {
gamePanel = new GamePanel(this,scale);
gamePanel.setVisible(true);
panel.add(gamePanel);
this.revalidate();}
}
GamePanel class
public class GamePanel extends JPanel {
private static final long serialVersionUID = 1L;
private int imgSize = 32;
public String gameState ="create_row";
private ImagePanel[][] mat_pos = new ImagePanel[6][11];
private Timer tiempo;//changed to a swing timer..
public GamePanel(GameFrame gp,int scale) {
this.setLayout(null);
createRow();//works fine if i call this method from here.
tiempo=new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
gameFlow();
}});
timerStart();
}
public void gameFlow() {//this is the method call by my timer
if (gameState.equals("create_row"))
{createRow(); //it doesnt render the created row!! this is my problem!!
gameState = "move_squares";
return;}
if (gameState.equals("move_squares"))
{moveSquares();return;}
}
private void moveSquares() {
int runningSquares = 0;
for (int col=0;col<6;col++)
for(int ren=10;ren>=0;ren--)
{if (mat_pos[col][ren]!=null)
if (mat_pos[col][ren].state.equals("running"))
{runningSquares++;
mat_pos[col][ren+1]=mat_pos[col][ren];
mat_pos[col][ren]=null;
if (ren+1==10 || mat_pos[col][ren+2]!=null)
mat_pos[col][ren+1].state = "stopped";
int newX,newY;
newX = imgSize * col;
newY = imgSize * (ren+1);
mat_pos[col][ren+1].setBounds(newX,newY, imgSize,imgSize);
}
}
if (runningSquares==0)
{gameState="create_row";
createRow();}
}
public void createRow() {//create 2 panels and add them to this jpanel.
gameState = "move_squares";
int colorRandom1 = (int) (Math.random()*5);
int colorRandom2 = (int) (Math.random()*5);
ImagePanel t1 = new ImagePanel(colorRandom1);
ImagePanel t2 = new ImagePanel(colorRandom2);
int columna = (int) (Math.random()*5);
mat_pos[columna][0] = t1;
mat_pos[columna+1][0] = t2;
t1.setBounds(columna*imgSize,0,32, 32);
t2.setBounds((columna+1)*imgSize,0,32, 32);
this.add(t1);
this.add(t2);
this.revalidate();
}
public void timerStart(){
tiempo.start();
}
public void timerStop(){
tiempo.stop();
}
}
//finallly the image panel class wich only creates a simple panel with an img as background
public class ImagePanel extends JPanel {
private static final long serialVersionUID = 1L;
public String state = "running";
private Image img;
private size = 1;
public ImagePanel(int color) {
String cad=null;
this.setVisible(true);
cad = "assets/blue.png";
this.img = new ImageIcon(cad).getImage();
crearFondo();
}
public void crearFondo() {
Dimension size = new Dimension(32,32);
setPreferredSize(size);
setMinimumSize(size);
setMaximumSize(size);
setSize(size);
setLayout(null);
}
public void paintComponent(Graphics g) {
g.drawImage(img, 0,0, null);
}
}
First of all, you generally need to revalidate after adding things to a container if it is already attached to the component hierarchy and visible.
Try calling this.revalidate() after the this.add() call in your createRow method.
Secondly, you seem to be using a java.util.Timer. This will not call you on the EDT. Use a javax.swing.Timer instead or make sure that createRow switches back to the EDT. This can be done with SwingUtilities.invokeLater(new Runnable() {...}). You should fix this no matter if my first suggestion works!
If that does not work, please provide a SSCCE, ideally one that I can copy-paste and try on my end.