在 java 实现 runnable 中使用线程时形状没有移动

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

我有这段代码,椭圆形在实现可运行对象时应该自动向右移动class。然而它似乎没有动。任何帮助深表感谢。提前致谢。

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);

调用线程分配球对象

        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);
                }
            }
        }

    }

您的线程只是更新程序内存(在每个阶段它都会递增 x)。窗口子系统不知道组件的脏状态,所以不调用 paint 方法。

您必须调用 JComponent.repaint(),请注意调用必须发生在 UI 线程中(例如使用 SwingUtilities.invokeLater())。

请注意,在这种情况下,您的程序没有任何 运行 顺利的机会,并且会爆发很多 CPU 循环。对于动画 and/or 游戏,您需要一个循环器来控制帧的 运行 时间以及一秒内的帧数。

这个...

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

使值不变...

这个...

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++;

是一个毫无意义的人。由于 Java 传递变量的方式,对值 x 的任何修改只会在 BallUsingThread class 的上下文中进行,MovingBall 将看不到他们,即使你可以让它重新粉刷。

相反,您可能应该将 MovingBall 的引用传递给 BallUsingThread 并提供一个 BallUsingThread 调用更新球的 x 位置的方法,例如...

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);
            }
        }
    }

}

现在,Swing 不是线程安全的(我已经说明了这一点),但是有一个更简单的解决方案...

改用 Swing Timer...

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();
    }

}

有关详细信息,请参阅 Concurrency in Swing and How to use Swing Timers