Andrew_Dublin
Andrew_Dublin

Reputation: 745

paint method not working inside while loop in swing

I have two classes. I try to move circle. When I type a break statement in Game.java it draws a circle for me without movement. That's fine. However when I want to move circle(by deleting break it displays nothing. I dont know how to debug that. I found out program is not going inside paint method when I use while without break; statement.
Can anybody also tell me the way how to debug problem like that. Thanks in advance

App.java

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class App extends JFrame {

    App() {

        JFrame jf = new JFrame(); 
        JPanel jp = new JPanel();
        jf.add(jp);
        jp.setLayout(new BorderLayout()); 
        jf.setSize(500, 500);
        jf.setTitle("Chain Reactor Game");
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.setVisible(true);

        Game game = new Game();
        jp.add(game, BorderLayout.CENTER);

        MenuBar menuBar = new MenuBar();
        jf.setJMenuBar(menuBar.createMenuBar());
    }

    public static void main(String[] args) {

        /*
         * oracle recommendation to run swing applications in special thread    
         * they suggest it during thread implementation
         */

        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new App();          
            }
        });
    }
}

Game.java

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;

import javax.swing.JPanel;

public class Game extends JPanel {

    private static final long serialVersionUID = 1L;
    int x = 0;
    int y = 0;

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        System.out.println("im goint to filloval now");
        g2d.fillOval(x, y, 30, 30);
    }
    public Game() {

        while(true) {

            moveBall();
            repaint();
            try {
                System.out.println("inside thread");
                Thread.sleep(2000);
            } 
            catch (InterruptedException e) {
                e.printStackTrace();
            }
        break;     // if i delete this line it not drawing. Why is that ?

        }
    }
    private void moveBall() {
        x = x + 1;
        y = y + 1;
    }
}

Upvotes: 0

Views: 3163

Answers (3)

MadProgrammer
MadProgrammer

Reputation: 347194

Let's break the code down...

The program starts here...

public static void main(String[] args) {

    /*
     * oracle recommendation to run swing applications in special thread    
     * they suggest it during thread implementation
     */

    SwingUtilities.invokeLater(new Runnable() {

        @Override
        public void run() {
            new App();          
        }
    });
}

This is good, it starts the GUI within the context of the Event Dispatching Thread, as described in Initial Threads...

Next, the App is constructed...

App() {

    JFrame jf = new JFrame(); 
    JPanel jp = new JPanel();
    jf.add(jp);
    jp.setLayout(new BorderLayout()); 
    jf.setSize(500, 500);
    jf.setTitle("Chain Reactor Game");
    jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    jf.setVisible(true);

    Game game = new Game();
    jp.add(game, BorderLayout.CENTER);

    MenuBar menuBar = new MenuBar();
    jf.setJMenuBar(menuBar.createMenuBar());
}

Okay, this "seems" okay, but if we take a closer look at Game

public Game() {

    while(true) {

        moveBall();
        repaint();
        try {
            System.out.println("inside thread");
            Thread.sleep(2000);
        } 
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    break;     // if i delete this line it not drawing. Why is that ?

    }
}

Ah, here's a problem...the while is been executed within the context of the Event Dispatching Thread, the EDT is used to process events and help schedule repaints, any thing that blocks the EDT will prevent it from, amongst other things, painting the UI

Start by taking a look at Concurrency in Swing

Now, you have a catch 22 situation here, Swing is a single threaded framework AND is NOT thread safe, this means that you can do long running/block process, but you also can't update the UI from outside the Event Dispatching Thread.

A simple solution in your case is to replace the while-loop with a javax.swing.Timer

public Game() {
    javax.swing.Timer timer = new javax.swing.Timer(2000, new javax.swing.ActionListener() {
        public void actionPerformed(javax.swing.ActionEvent evt) {
            moveBall();
            repaint();
        }
    });
    timer.start();
}

Take a look at How to use Swing Timers for more details...

As a side note, it's typically recommended to use paintComponent to perform custom painting over paint. Painting is a complex series of method calls, anything you can do to prevent screwing it up will keep your hair in place...

Have a look at Painting in AWT and Swing and Performing Custom Painting for more details

Upvotes: 4

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285403

You need to get that while loop out of your Swing event thread as it's tying up the thread preventing Swing from performing its drawing functions or user interactions. You're basically stepping on the Swing event thread, violating Swing concurrency rules. Use a Swing Timer instead for simple animations which was built for this sort of thing.

Upvotes: 4

DanielGibbs
DanielGibbs

Reputation: 10183

When you leave the break; in you have an infinite loop. It will never end, meaning that the line Game game = new Game(); will never return, meaning that the next line, which adds the Game to the JPanel will never run, which is why nothing is drawn on the screen. To get this to work, you need to create the GUI, and then perform the drawing.

One way to get this to work is to create a new Thread to perform the continual drawing:

public Game() {
    (new Thread() {
        public void run() {
            draw();
        }
     ).start();
 }

private void draw() {
    while(true) {
        moveBall();
        repaint();
        try {
            System.out.println("inside thread");
            Thread.sleep(2000);
        } 
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Alternatively, since you are setting up the GUI on the Swing thread, you could run the drawing on the main thread.

Upvotes: 2

Related Questions