1

Hello, I've been working on a GUI application that should resize dynamically and change background color multiple times after clicking a button. My first shot was to make a for loop inside an actionPerformed(ActionEvent e) method but it doesn't work properly. Strangely enough, the app changes properly if the listener is removed and the commented code from main function is used. Why is that?

This code is actually a simple example of a problem which I've came across making a slightly more complex app. Could You kindly add a line or two if the solution varies if my window consists of many JPanels and I decide to repeatedly change properties of some other elements with one button click?
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;


public class TempWindow {

    public static void main(String [] args ) {
        Frame f = new Frame();

//      for (int i = 0; i < 3; i++) {
//          if(i == 0) {
//          f.getMyPanel().setBackground(Color.red);
//          }
//          if (i == 1) {
//          f.setSize(500, 400);
//          f.getMyPanel().setBackground(Color.yellow);
//          }
//          if( i == 2) {
//              f.setSize(100, 200);
//              f.getMyPanel().setBackground(Color.green);
//          }
//          
//          try {
//              Thread.sleep(500);
//          } catch (InterruptedException e) {
//              e.printStackTrace();
//          }
//          f.repaint();
//      }

    }

}

class Frame extends JFrame {

    MyPanel panel;
    JButton but1;

    public Frame() {

        this.setSize(400, 300);
        this.setLocationRelativeTo(null);
        this.setLayout(new BorderLayout());
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);

        but1 = new JButton("Start");
        this.getContentPane().add(but1, BorderLayout.NORTH);

        panel = new MyPanel();
        this.getContentPane().add(panel, BorderLayout.CENTER);
        but1.addActionListener(new DanceActionListener(this));

        this.setVisible(true);
    }

    public MyPanel getMyPanel() {
        return panel;
    }
}

class MyPanel extends JPanel {

    JLabel text;

    public MyPanel() {
        this.setBackground(Color.black);
        this.setLayout(new BorderLayout());
        text  = new JLabel(" xxx ");
        text.setFont(new Font(Font.SERIF, Font.BOLD, 40));
        this.add(text, BorderLayout.CENTER);

        text.setHorizontalAlignment(SwingConstants.CENTER);
        text.setVerticalAlignment(SwingConstants.CENTER);
    }
}

class DanceActionListener implements ActionListener {

    MyPanel panel;
    Frame f;

    public DanceActionListener(Frame f) {
        this.f = f;
        this.panel = f.getMyPanel();
    }

    public void actionPerformed(ActionEvent e ){ 
        for (int i = 0; i < 3; i++) {
            if(i == 0) {
                panel.setBackground(Color.red);
                panel.repaint();
            }
            if (i == 1) {
                panel.setBackground(Color.yellow);
                f.setSize(500, 400);
            }
            if( i == 2) {
                f.setSize(100, 200);
                panel.setBackground(Color.green);
            }

            f.repaint();

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
        }
    }

}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
musztard2000
  • 127
  • 2
  • 14
  • 1
    `Thread.sleep(1000);` Whatever you are trying to achieve there, this is the wrong way to go about it. Don't block the EDT (Event Dispatch Thread). The GUI will 'freeze' when that happens. See [Concurrency in Swing](https://docs.oracle.com/javase/tutorial/uiswing/concurrency/) for details and the fix. – Andrew Thompson Nov 17 '14 at 09:55
  • 3
    Take a look at [Concurrency in Swing](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/), [How to use Swing Timers](http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html) and [Worker Threads and SwingWorker](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html) – MadProgrammer Nov 17 '14 at 09:56
  • 3
    The reason it works in the `main` is because the UI has been moved to another thread (the EDT), but you should never modify the UI from outside of the context of the EDT – MadProgrammer Nov 17 '14 at 09:57
  • 1
    @MadProgrammer I reckon those 2 comments together are the makings of an answer.. – Andrew Thompson Nov 17 '14 at 09:59
  • BTW - `Frame f = new Frame();` should be `JFrame f = new JFrame();` for a Swing app. – Andrew Thompson Nov 17 '14 at 10:15
  • Please see http://stackoverflow.com/questions/4396583/java-swing-repaint-vs-invalidate – Jean-Baptiste Yunès Nov 17 '14 at 10:48

0 Answers0