Search code examples
javaanimationjcomponent

Repaint won't happen until the for loop has escaped, with only two frames


I'm trying to do some basic Java, and I've got my frame with a shape in it. It's using a JComponent class to draw the shape, with the animation being triggered on a button click at the top.

the component code is just this added to the jpanel

public void paintComponent(Graphics g){     
    Dimension dim = getSize();      
    g.setColor(Color.GREEN);
    g.fillOval(margin, 150, 100, 100);  
    super.paintComponent(g);
}

The animation is just done inside a for loop, that just edits the left margin so that the circle moves to the right;

int getMarg = cc.getMargin();
            for(int i = 1;i < 20;i++){  
                getMarg = cc.getMargin();
                cc.setMargin(getMarg + 1);              
                reValidate();
                System.out.println(i);

But it doesn't seem to move until the end of the loop, moving 20 pixels at a time. I previously had a sleep function but it seemed pointless when it wouldn't animate.

Any insight? Cheers.

The whole code for anyone interested, messy and largely just to get the styling:

class Main extends JFrame{

public JPanel panel = new JPanel();
JButton button1 = new JButton("Move Right");

CreateComps cc = new CreateComps();

Main(){
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    initUI();
}

void initUI(){
    setSize(800,800);
    setBackground(Color.GRAY);
    setLayout(new BoxLayout(this.getContentPane(), BoxLayout.Y_AXIS));

    JPanel topBar = new JPanel();
    topBar.setPreferredSize(new Dimension(800,30));
    topBar.setMaximumSize(new Dimension(800,30));
    topBar.setLayout(new BorderLayout());
    topBar.add(button1, BorderLayout.WEST);
    topBar.setBackground(Color.GRAY);

    JPanel container = new JPanel();
    container.setLayout(new GridBagLayout());
    container.setBackground(Color.DARK_GRAY);

    panel.setPreferredSize(new Dimension(600,500));
    panel.setMinimumSize(new Dimension(600,500));
    panel.setBackground(Color.WHITE);
    panel.setLayout(new BorderLayout());
    panel.add(cc, BorderLayout.CENTER);

    add(topBar);
    add(container);
    container.add(panel);

    Listener listen = new Listener();
    button1.addActionListener(listen);

    setVisible(true);
}

public void reValidate(){
    panel.revalidate();
    panel.repaint();
}

public static void main (String[] args){
    Main main = new Main();
}

class Listener implements ActionListener{
    @Override
    public void actionPerformed(ActionEvent e) {

        System.out.println("Listening..");
        if(e.getSource().equals(button1)){
            int getMarg = cc.getMargin();
            for(int i = 1;i < 20;i++){  
                getMarg = cc.getMargin();
                cc.setMargin(getMarg + 1);              
                reValidate();
                System.out.println(i);
            }
        }                       
    }   
}



}

class CreateComps extends JComponent{

int margin = 10;        
public void setMargin(int marg){
    margin = marg;
}   
public int getMargin(){
    return margin;
}   
@Override
public Dimension getPreferredSize(){
    return new Dimension(new Dimension(200,200));
}   
@Override
public Dimension getMaximumSize(){
    return new Dimension(new Dimension(200,200));
}   
@Override
public Dimension getMinimumSize(){
    return new Dimension(new Dimension(200,200));
}   
public void paintComponent(Graphics g){     
    Dimension dim = getSize();      
    g.setColor(Color.GREEN);
    g.fillOval(margin, 150, 100, 100);  
    super.paintComponent(g);
}

}


Solution

  • Without pauses, you're stacking calls to revalidate and will see nothing else than the result of the last call.

    The sleep function you previously had, probably was called on the Event Dispatch Thread, which is not good at all, since you're blocking the entire event dispatching and GUI updates.

    Consider either using sleep calls on another Thread :

    @Override
    public void actionPerformed(final ActionEvent e) {
    
        System.out.println("Listening..");
        if (e.getSource().equals(button1)) {
            new Thread() {
                @Override
                public void run() {
                    int getMarg = cc.getMargin();
                    for (int i = 1; i < 20; i++) {
                        getMarg = cc.getMargin();
                        cc.setMargin(getMarg + 1);
                        reValidate();
                        System.out.println(i);
    
                        try {
                            Thread.sleep(50);
                        } catch (Throwable e) {
                        }
    
                    }
                }
            }.start();
    
        }
    
    }
    

    Or you may also simply use a Timer, which is great for this job.