Java Swing - paintComponent() 没有绘制我的线程

Java Swing - paintComponent() not drawing my Threads

我想向 Thread 中的 ArrayList 添加一个新球 Thread 每5秒。 所以我的 JPanel 实现了 Runnable 并且我在 ArrayList 中添加了一个新球 Thread。 在 paintComponent() 方法中,我使用 foreach 循环迭代球 ThreadArrayList 并启动它们的 Thread 移动它们并调用 repaint().问题是我根本看不到任何绘图(只有玩家绘图)。

MyPanel.java:

public class MyPanel extends JPanel implements KeyListener,Runnable
{
    private static final long serialVersionUID = 1L;

    private static final Color BACKGROUND_COLOR = Color.WHITE; 
    private static final Color NPC_BALLS_COLOR = Color.RED;

    // The player is an oval
    private int playerRadius = 20;
    private int playerX;
    private int playerY;

    // True - first player position, false - otherwise
    private boolean playerPosition = true;

    // Array of all the balls threads
    private ArrayList<BallThread> balls = new ArrayList<BallThread>();

    private Thread generateBallsThread;

    public MyPanel()
    {
        this.setBackground(MyPanel.BACKGROUND_COLOR);
        this.setFocusable(true);
        this.addKeyListener(this);

        this.generateBallsThread = new Thread();
        generateBallsThread.start();
    }


    // Drawing

    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);

        final double PANEL_WIDTH = this.getWidth();
        final double PANEL_HEIGHT = this.getHeight();

        if (this.playerPosition)
        {
            this.playerX = (int)(PANEL_WIDTH / 2 - this.playerRadius);
            this.playerY = (int)(PANEL_HEIGHT / 2 - this.playerRadius);
            this.playerPosition = false;
        }

        // Drawing the player
        g.setColor(Color.BLACK);
        g.fillOval(playerX,playerY, this.playerRadius * 2, this.playerRadius * 2);

        // Drawing npc's balls
        g.setColor(MyPanel.NPC_BALLS_COLOR);
        for (BallThread ball: this.balls)
        {
            ball.start();
            g.fillOval(ball.getBall().getX(), ball.getBall().getY(), 
                    ball.getBall().radius, ball.getBall().radius);
            repaint();
        }

    }

    // Keyboard listeners

    @Override
    public void keyPressed(KeyEvent e) 
    {
        switch (e.getKeyCode())
        {
            case KeyEvent.VK_W: // Up
            {
                this.playerY -= 5;
                repaint();
                break;
            }
            case KeyEvent.VK_S: // Down
            {
                this.playerY += 5;
                repaint();
                break;
            }
            case KeyEvent.VK_D: // Right
            {
                this.playerX += 5;
                repaint();
                break;
            }
            case KeyEvent.VK_A: // Left
            {
                this.playerX -= 5;
                repaint();
                break;
            }
        }
    }

    @Override
    public void keyReleased(KeyEvent e) 
    {

    }

    @Override
    public void keyTyped(KeyEvent e) 
    {

    }

    public int getBallXStartingPositionFromTop()
    {
        return (int) Math.random() * 101; // 0 - 100
    }

    //public int getBallYStartingPositionFromLeft()
    //{
    //  return (int) Math.random() * 101; // 0 - 100
    //}

    /**
     * 
     * 
     * 
     * Class for the balls threads.
     *
     */
    public class BallThread extends Thread
    {
        private Ball ball;

        public BallThread(Ball ball)
        {
            this.ball.setX(ball.getX());
            this.ball.setY(ball.getY());
        }

        @Override
        public void run() 
        {
            try 
            {
                this.ball.move();
                Thread.sleep(4000);
                repaint(); // Execute paintComponent() method
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }

        public Ball getBall()
        {
            return this.ball;
        }

        public void setBall(Ball ball) 
        {
            this.ball = ball;
        }


    }

    @Override
    public void run()
    {
        try
        {
            Thread.sleep(5000); // 5 seconds
            this.balls.add(new BallThread(new Ball(20,20)));

        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }








} // End of MyPanel class

Ball.java:

public class Ball 
{
    int x;
    int y;
    int velocity = 1;
    public int radius = 10;
    public boolean directionX = true; // True - right, false - left
    public boolean directionY = false; // True - up, false - down

    public static final int Y_STARTING_POSITION_FROM_TOP = 0;

    public Ball(int x, int y) 
    {
        this.x = x;
        this.y = y;
    }

    public Ball()
    {

    }

    public int getX() 
    {
        return this.x;
    }
    public void setX(int x) 
    {
        this.x = x;
    }
    public int getY()
    {
        return this.y;
    }
    public void setY(int y) 
    {
        this.y = y;
    }

    public void move()
    {
        if (this.directionX) // Right
        {
            this.x += this.velocity;
        }
        else // Left
        {
            this.x -= this.velocity;
        }
        if (this.directionY) // Up
        {
            this.y -= this.velocity;
        }
        else
        {
            this.y += this.velocity;
        }
    }


}

我省略了一个简单的 JFrame class 和一个 main() 生成 JFrame 添加了 JPanel

为什么我看不到 Thread 的球图?

    this.generateBallsThread = new Thread();
    generateBallsThread.start();

那什么都不做。这是一个没有逻辑的空线程。

我想你想要

    this.generateBallsThread = new BallsThread();
    generateBallThread.start();

此外,您不应该使用线程来制作动画。您应该使用 Swing Timer。任何更新 Swing 组件状态的逻辑都应该在事件调度线程上执行。阅读 Concurrency 上的 Swing 教程部分以获取更多信息。本教程还有一个关于 How to Use Swing Timers.

的部分