quantum285
quantum285

Reputation: 1032

invokeLater not working as expected (JButton never releasing)

I have:

And I wish to, at the press of the JButton bring up a new JFrame displaying the Canvas subclass as it animates.

The problem I face right now is that the new JFrame appears, however it doesn't get a chance to render anything and the JButton on the main frame stays depressed. The logic I figure behind this is that the EDT hasn't finished doing it's jobs such as showing the JButton as released and so does not get a chance to run the animation method and ends up in deadlock.

This logic treated me well in the past as I made this work by creating a new thread, but having learned more about Java, threads and Swing lately I've come to know that all Swing related events must be handled on one thread: the EDT.

This confuses me as to how I got it working before but lead me to believe that using invokeLater would help the problem; as the job of making the JFrame visible and showing animation would be placed at the end of the queue allowing the JButton to unrelease etc. I've had no luck however; have I completely misunderstood something?

Thanks!

(Also please no comments on my use of the Canvas class as opposed to JPanel, I have my reasons).

Sample code:

Test5 (class with main method).

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*

public class Test5 {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
              public void run() {
                  new Test5().setup();
              }
            });
          }
    private void setup() {
        JFrame frame = new JFrame("Test");
        JButton button = new JButton("Click here");
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        newFrame();
                    }
                  });
            }
        });
        frame.getContentPane().add(button);
        frame.pack();
        frame.setVisible(true);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }
    private void newFrame() {
        JFrame newFrame = new JFrame("The new frame");
        newFrame.setVisible(true);
        newFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        CanvasClass canvas = new CanvasClass();
        newFrame.getContentPane().add(canvas);
        newFrame.pack();
        canvas.runAnimation();
    }
}

CanvasClass (Canvas subclass)

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;


public class CanvasClass extends Canvas {
    int x;
    public CanvasClass() {
        setSize(new Dimension(550,550));
        this.x = (int) (Math.random() * 255);
    }

    //@Override
    public void paint(Graphics g) {
        g.setColor(new Color(x, x, x));
        g.fillOval(0,0,500,500);
    }

    void runAnimation() {
        while (true) {
            randomise();
            repaint();
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    void randomise() {
    x = (int) (Math.random() * 255);
    }
}

Upvotes: 0

Views: 263

Answers (1)

StanislavL
StanislavL

Reputation: 57381

You actualy invoke it in EDT but it's blocked in the canvas.runAnimation();

Place the code to be executed in a separate Thread (where you can call sleep) but call the repaint() in SwingUtilities.invokeLater()

Or even better to define a javax.swing.Timer and call the runAnimation() in the Timer's actionPerformed()

UPDATE:

  int delay = 20; //milliseconds
  ActionListener taskPerformer = new ActionListener() {
      public void actionPerformed(ActionEvent evt) {
          canvasInstance.randomise();
          canvasInstance.repaint();
      }
  };
  new Timer(delay, taskPerformer).start();

to be called instead of the runAnimation()

Upvotes: 3

Related Questions