Jay Gorio
Jay Gorio

Reputation: 335

The shape is not moving when using thread in java implementing runnable

I have this code, that the oval shape should automatically move to the right when implementing the runnable class. However it seems not moving. Any help is much appreciated. Thanks in advance.

package movingball;

import java.awt.Color;
import java.awt.Graphics;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class MovingBall extends JPanel{

    private static final int x = 30;
    private static final int y = 30;

    public MovingBall(){
        setBackground(Color.BLACK);
    }
    public MovingBall(int x, int y){
        x = this.x;
        y = this.y;  
        repaint();
    }
    public static void main(String[] args) {
        JFrame frame = new JFrame();

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(500,700);

        MovingBall movingBall = new MovingBall();
        frame.add(movingBall);       
        frame.setVisible(true);

Calling the thread to assign the ball object

        BallUsingThread ball =  new BallUsingThread(x, y);
        Thread first = new Thread(ball);
        first.start();

    }

    @Override
    public void paintComponent(Graphics canvas){
        super.paintComponent(canvas);

        canvas.setColor(Color.BLUE);
        canvas.fillOval(x, y, 100, 100);
    }

}
/*Here is the second class. Where the oval shape should be moving. Any `suggestions here? Also just let me know if there are some codes need to be adjusted.*/

    class BallUsingThread implements Runnable{
        int x = 30;
        int y = 30;

        public BallUsingThread(int x, int y){
            this.x  = x;
            this.y = y;
        }
        @Override
        public void run() {
            for(;;){
                x++;
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException ex) {
                    System.out.printf("Error",ex);
                }
            }
        }

    }

Upvotes: 0

Views: 680

Answers (2)

MadProgrammer
MadProgrammer

Reputation: 347314

This...

private static final int x = 30;
private static final int y = 30;

makes the values unchangable...

This...

class BallUsingThread implements Runnable{
    int x = 30;
    int y = 30;

    public BallUsingThread(int x, int y){
        this.x  = x;
        this.y = y;
    }
    @Override
    public void run() {
        for(;;){
            x++;

is someone pointless. Because of the way Java pass variables, any modifications to the value x will only be made within the context of the BallUsingThread class, MovingBall would not see them, even if you could get it to repaint.

Instead, you should probably pass a reference of MovingBall to BallUsingThread and provide a method which BallUsingThread call call which updates the x position of the ball, for example...

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class MovingBall extends JPanel {

    private int ballX = 30;
    private int ballY = 30;

    public MovingBall() {
        setBackground(Color.BLACK);
    }

    public MovingBall(int x, int y) {
        x = this.ballX;
        y = this.ballY;
        repaint();
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame();

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(500, 700);

        MovingBall movingBall = new MovingBall();
        frame.add(movingBall);
        frame.setVisible(true);

        BallUsingThread ball = new BallUsingThread(movingBall);
        Thread first = new Thread(ball);
        first.start();

    }

    @Override
    public void paintComponent(Graphics canvas) {
        super.paintComponent(canvas);

        canvas.setColor(Color.BLUE);
        canvas.fillOval(ballX, ballY, 100, 100);
    }

    public void updateBall() {
        if (EventQueue.isDispatchThread()) {
            ballX++;
            repaint();
        } else {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    updateBall();
                }
            });
        }
    }

}

/*Here is the second class. Where the oval shape should be moving. Any `suggestions here? Also just let me know if there are some codes need to be adjusted.*/
class BallUsingThread implements Runnable {

    private MovingBall movingBall;

    public BallUsingThread(MovingBall mb) {
        movingBall = mb;
    }

    @Override
    public void run() {
        for (;;) {
            movingBall.updateBall();
            try {
                Thread.sleep(500);
            } catch (InterruptedException ex) {
                System.out.printf("Error", ex);
            }
        }
    }

}

Now, Swing is not thread safe (which I've accounted for), but there is a simpler solution...

Use a Swing Timer instead...

MovingBall

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class MovingBall extends JPanel {

    private int ballX = 30;
    private int ballY = 30;

    public MovingBall() {
        setBackground(Color.BLACK);
    }

    public MovingBall(int x, int y) {
        x = this.ballX;
        y = this.ballY;
        repaint();
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame();

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(500, 700);

        MovingBall movingBall = new MovingBall();
        frame.add(movingBall);
        frame.setVisible(true);

        BallUsingTimer ball = new BallUsingTimer(movingBall);
        Timer timer = new Timer(40, ball);
        timer.start();
    }

    @Override
    public void paintComponent(Graphics canvas) {
        super.paintComponent(canvas);

        canvas.setColor(Color.BLUE);
        canvas.fillOval(ballX, ballY, 100, 100);
    }

    public void updateBall() {
        ballX++;
        repaint();
    }

}

BallUsingTimer

public class BallUsingTimer implements ActionListener {

    private MovingBall movingBall;

    public BallUsingTimer(MovingBall mb) {
        movingBall = mb;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        movingBall.updateBall();
    }

}

See Concurrency in Swing and How to use Swing Timers for more details

Upvotes: 2

Raffaele
Raffaele

Reputation: 20885

Your thread simply updates the program memory (at each stage it increments x). The windowing subsystem is not aware of the dirty state of the component, so the paint method is not called.

You must call JComponent.repaint() and please note that the call must happen in the UI thread (for example by using SwingUtilities.invokeLater()).

Note that in this way your program doesn't have any chance of running smoothly, and bursts a lot of CPU cycles. For animations and/or games you need a looper that lets you control the running time of the frames as well as the number of frames in one second.

Upvotes: 0

Related Questions