动画:移动时隐藏的对象(JAVA、图形、动画)

Animation: objects hidden while moving (JAVA, GRAPHICS, ANIMATION)

我正在构建一个图表,其中有一个圆圈沿着一条线移动,并在路径的 3 个点停留几秒钟。 我设法做到了,但是,它不显示圆圈移动,它仅在圆圈停止时显示... 我不明白为什么。

非常感谢您的帮助

这是我的代码:

public class Robot0 extends JFrame implements ActionListener {

    public Robot0(String nom, int larg, int haut) {
        setTitle(nom); 
        setSize(larg, haut); 
        setLocationRelativeTo(null); 
        setResizable(false); 
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);                     
        setVisible(true); 
    }

    Timer tm = new Timer(10, this);

    private int posX = 0; 
    private int posY = 0;
    private int velX = 1;

    public int getPosX() {
        return posX;
    }

    public void setPosX(int posX) {
        this.posX = posX;
    }

    public int getPosY() {
        return posY;
    }

    public void setPosY(int posY) {
        this.posY = posY;
    }

    public void paint(Graphics g) {
        super.paint(g);
        g.setColor(Color.BLACK);
        // Draw the pathway 
        int xt[] = { 50, 50, 250, 250, 350, 350 };
        int yt[] = { 50, 150, 150, 50, 50, 150 };
        g.drawPolyline(xt, yt, 6);

        // On the pathway, draw 3 squares (the 3 rooms)
        g.setColor(Color.GREEN);
        g.drawRect(35, 135, 30, 30);
        g.drawRect(235, 35, 30, 30);
        g.drawRect(335, 135, 30, 30);

        g.setColor(Color.WHITE);
        g.fillOval(40 + posX, 40 + posY, 20, 20);

        g.setColor(Color.RED);
        g.drawLine(45 + posX, 50 + posY, 55 + posX, 50 + posY);
        g.drawLine(50 + posX, 45 + posY, 50 + posX, 55 + posY);

        tm.start();
    }

    int segment = 0;

    public void actionPerformed(ActionEvent e) {

        // move along the 1st segment
        if (posY < 100 && segment == 0) {
            setPosY(posY + velX);
        }

        if (posY == 100 && posX == 0) {
            segment = 1;
            try {
                Thread.sleep(15000);
            } catch (InterruptedException ex) {
                Logger.getLogger(Robot0.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        // move along the second segment
        if (posX <= 200 && segment == 1) {
            setPosX(posX + velX);
        }

        if (posX == 200 && posY == 100) {
            segment = 2;
        }

        // move along the third segment
        if (posY > 0 && segment == 2) {
            setPosY(posY - velX);
        }

        if (posX == 200 && posY == 0) {
            segment = 3;
            try {
                Thread.sleep(15000);
            } catch (InterruptedException ex) {
                Logger.getLogger(Robot0.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        // move along the fourth segment
        if (posX < 300 && segment == 3) {
            setPosX(posX + velX);
        }

        if (posX == 300 && posY == 0) {
            segment = 4;
        }

        // move along the fifth segment
        if (posY < 100 && segment == 4) {
            setPosY(posY + velX);
        }

        if (posX == 300 && posY == 100) {
            segment = 6;
            try {
                Thread.sleep(15000);
            } catch (InterruptedException ex) {
                Logger.getLogger(Robot0.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        repaint();
    }

// Build the Panel
    public static void main(String[] args) {
        Robot0 r = new Robot0("Robot0", 800, 600);
    }

}

Swing 是一个单线程库。所有的绘画任务都在Event Dispatcher Thread中执行 (EDT)。
正如 Andrew Thompson 评论的那样,EDT 上的 运行 长进程(例如睡眠)使该线程保持忙碌,因此它不会做其他事情 比如更新gui。
gui 变得没有响应(冻结)。 所以首先要做的就是去掉所有的睡觉。
要在每个站点停车,请使用第二个计时器:

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.SwingUtilities;
import javax.swing.Timer;

public class Robot0 extends JFrame implements ActionListener {

    private static final int PARKING_TIME = 15000;
    Timer moveTimer , waitTimer;
    private boolean isParking = false;

    public Robot0(String nom, int larg, int haut) {
        moveTimer = new Timer(10, this);
        waitTimer = new Timer(PARKING_TIME, e-> isParking = false);
        waitTimer.setRepeats(false);

        setTitle(nom);
        setSize(larg, haut);
        setLocationRelativeTo(null);
        setResizable(false);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
    }

    private int posX = 0;
    private int posY = 0;
    private final int velX = 1;

    public int getPosX() {
        return posX;
    }

    public void setPosX(int posX) {
        this.posX = posX;
    }

    public int getPosY() {
        return posY;
    }

    public void setPosY(int posY) {
        this.posY = posY;
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        g.setColor(Color.BLACK);
        // Draw the pathway
        int xt[] = { 50, 50, 250, 250, 350, 350 };
        int yt[] = { 50, 150, 150, 50, 50, 150 };
        g.drawPolyline(xt, yt, 6);

        // On the pathway, draw 3 squares (the 3 rooms)
        g.setColor(Color.GREEN);
        g.drawRect(35, 135, 30, 30);
        g.drawRect(235, 35, 30, 30);
        g.drawRect(335, 135, 30, 30);

        g.setColor(Color.WHITE);
        g.fillOval(40 + posX, 40 + posY, 20, 20);

        g.setColor(Color.RED);
        g.drawLine(45 + posX, 50 + posY, 55 + posX, 50 + posY);
        g.drawLine(50 + posX, 45 + posY, 50 + posX, 55 + posY);

        moveTimer.start();
    }

    int segment = 0;

    @Override
    public void actionPerformed(ActionEvent e) {

        if(isParking) return; //execute only when not parking 

        // move along the 1st segment
        if (posY < 100 && segment == 0) {
            setPosY(posY + velX);
        }

        if (posY == 100 && posX == 0 && segment != 1) { //!=1 so it will not be invoked again 
            segment = 1;
            isParking = true; //flag that robot is parking 
            waitTimer.start();
            return;
        }

        // move along the second segment
        if (posX <= 200 && segment == 1) {
            setPosX(posX + velX);
        }

        if (posX == 200 && posY == 100) {
            segment = 2;
        }

        // move along the third segment
        if (posY > 0 && segment == 2) {
            setPosY(posY - velX);
        }

        if (posX == 200 && posY == 0 && segment !=3) {
            segment = 3;
            isParking = true;
            waitTimer.start();
            return;
        }

        // move along the fourth segment
        if (posX < 300 && segment == 3) {
            setPosX(posX + velX);
        }

        if (posX == 300 && posY == 0) {
            segment = 4;
        }

        // move along the fifth segment
        if (posY < 100 && segment == 4) {
            setPosY(posY + velX);
        }

        if (posX == 300 && posY == 100 && segment !=6) {
            segment = 6;
            isParking = true;
            waitTimer.start();
            return;
        }
        repaint();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(()->new Robot0("Robot0", 800, 600));
    }
}

待办事项:

  1. 在JPanel 上实现自定义绘画。
  2. 简化actionPerformed 逻辑

编辑: 以下是一个实现,其中有一些改进不一定与问题相关:

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

public class Robot0 extends JFrame {

    public Robot0(String nom) {
        setTitle(nom);
        setLocationRelativeTo(null);
        setResizable(false);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        add(new Floor());
        pack();
        setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(()->new Robot0("Robot0"));
    }
}

class Floor extends JPanel implements ActionListener{

    private static final int PARKING_TIME = 5000, REPAINT_TIME = 10, W = 400, H = 200;
    private static final int ROOM_SIZE = 30, ROBOT_SIZE = 20, CROSS_SIZE = 10;

    private final Timer moveTimer , waitTimer;
    private boolean isParking = false;

    private int posX = 0, posY = 0;
    private final int velX = 1;

    // pathway
    private static final int PATH_X[] = { 50,  50, 250, 250, 350,  350 };
    private static final int PATH_Y[] = { 50, 150, 150,  50,  50,  150 };
    //rooms
    private static final Point[] ROOM_CENTERS = {new Point(PATH_X[1],PATH_Y[1]),
                                                 new Point(PATH_X[3],PATH_Y[3]),
                                                 new Point(PATH_X[5],PATH_Y[5]) };

    Floor() {
        moveTimer = new Timer(REPAINT_TIME, this);
        waitTimer = new Timer(PARKING_TIME, e-> isParking = false);
        waitTimer.setRepeats(false);
        posX = PATH_X[0]; posY = PATH_Y[0];
        setPreferredSize(new Dimension(W, H));
        moveTimer.start(); //no need to restart with every paint
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.BLACK);

        g.drawPolyline(PATH_X, PATH_Y, 6);

        // draw rooms
        g.setColor(Color.GREEN);
        for(Point center : ROOM_CENTERS){
            drawSquareAround(center, g);
        }

        //robot
        g.setColor(Color.WHITE);
        g.fillOval( posX - ROBOT_SIZE/2 , posY - ROBOT_SIZE/2 , ROBOT_SIZE, ROBOT_SIZE);

        //cross
        g.setColor(Color.RED);
        g.drawLine(posX - CROSS_SIZE/2, posY, posX + CROSS_SIZE/2, posY);
        g.drawLine(posX, posY - CROSS_SIZE/2, posX,  posY + CROSS_SIZE/2);
    }


    private void drawSquareAround(Point center, Graphics g) {
        g.drawRect(center.x - ROOM_SIZE/2, center.y - ROOM_SIZE/2, ROOM_SIZE, ROOM_SIZE);
    }

    @Override
    public void actionPerformed(ActionEvent e) {

        if(isParking) return; //execute only when not parking

        if (posX <= PATH_X[0]  &&  posY < PATH_Y[1]) {// move along the 1st segment
            setPosY(posY + velX);
        }else if (posX < PATH_X[2] && posY == PATH_Y[1]) { //move along the second segment
            setPosX(posX + velX);
        }else  if (posX == PATH_X[2] && posY > PATH_Y[3]) { //move along the third segment
            setPosY(posY - velX);
        }else if (posY == PATH_Y[3] && posX < PATH_X[4]) {// move along the fourth segment
                setPosX(posX + velX);
        }else if (posX == PATH_X[4] && posY < PATH_Y[5]){// move along the fifth segment
            setPosY(posY + velX);
        }else {
            moveTimer.stop(); //move finished, stop repainting
            return;
        }

        //park if at room center
        if(isRoomCeter()){
            park();
        }
        repaint();
    }

    private void park() {
      isParking = true; //flag that robot is parking
      waitTimer.start();
    }

    private boolean isRoomCeter() {
        for (Point center : ROOM_CENTERS){
            if(posX == center.x && posY == center.y) return true;
        }
        return false;
    }

    public int getPosX() {
        return posX;
    }

    public void setPosX(int posX) {
        this.posX = posX;
    }

    public int getPosY() {
        return posY;
    }

    public void setPosY(int posY) {
        this.posY = posY;
    }
}