Reputation: 335
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
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
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