Java,程序最后只重新绘制()

Java,程序最后只重新绘制(),java,repaint,Java,Repaint,我必须在学校做一个作业,它是一个停车场模拟器,叫做run();从main提供了一个完全正常的模拟,其中Carspot每一步都被重新绘制,但是当从actionListener调用它时,它只绘制结果,而不绘制中间的步骤。下面有6个类,其中main在Simulator类中,SimulatorView具有repaint();在里面 那么,有谁能向我解释一下,为什么它在从main调用run()时模拟每个步骤,为什么它在从JButton后面的ActionListener调用run()时只绘制输出 Simul

我必须在学校做一个作业,它是一个停车场模拟器,叫做run();从main提供了一个完全正常的模拟,其中Carspot每一步都被重新绘制,但是当从actionListener调用它时,它只绘制结果,而不绘制中间的步骤。下面有6个类,其中main在Simulator类中,SimulatorView具有repaint();在里面

那么,有谁能向我解释一下,为什么它在从main调用run()时模拟每个步骤,为什么它在从JButton后面的ActionListener调用run()时只绘制输出

Simulator.java:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

public class Simulator implements ActionListener{

private CarQueue entranceCarQueue;
private CarQueue paymentCarQueue;
private CarQueue exitCarQueue;
private SimulatorView simulatorView;

private int day = 0;
private int hour = 0;
private int minute = 0;

private int tickPause = 100;

int weekDayArrivals= 50; // average number of arriving cars per hour
int weekendArrivals = 90; // average number of arriving cars per hour

int enterSpeed = 3; // number of cars that can enter per minute
int paymentSpeed = 10; // number of cars that can pay per minute
int exitSpeed = 9; // number of cars that can leave per minute

public Simulator() {
    entranceCarQueue = new CarQueue();
    paymentCarQueue = new CarQueue();
    exitCarQueue = new CarQueue();
    simulatorView = new SimulatorView(3, 6, 30, this);
}

public void run() {
    for (int i = 0; i < 10000; i++) {
        tick();
    }
}

public void run(int steps) {
    System.out.println("run " + steps + " steps");
    for (int i = 0; i < steps; i++) {
        tick();
    }
}

private void tick() {
    System.out.println("simulator-tick");
    // Advance the time by one minute.
    minute++;
    while (minute > 59) {
        minute -= 60;
        hour++;
    }
    while (hour > 23) {
        hour -= 24;
        day++;
    }
    while (day > 6) {
        day -= 7;
    }

    Random random = new Random();

    // Get the average number of cars that arrive per hour.
    int averageNumberOfCarsPerHour = day < 5
            ? weekDayArrivals
            : weekendArrivals;

    // Calculate the number of cars that arrive this minute.
    double standardDeviation = averageNumberOfCarsPerHour * 0.1;
    double numberOfCarsPerHour = averageNumberOfCarsPerHour + random.nextGaussian() * standardDeviation;
    int numberOfCarsPerMinute = (int)Math.round(numberOfCarsPerHour / 60);

    // Add the cars to the back of the queue.
    for (int i = 0; i < numberOfCarsPerMinute; i++) {
        Car car = new AdHocCar();
        entranceCarQueue.addCar(car);
    }

    // Remove car from the front of the queue and assign to a parking space.
    for (int i = 0; i < enterSpeed; i++) {
        Car car = entranceCarQueue.removeCar();
        if (car == null) {
            break;
        }
        // Find a space for this car.
        Location freeLocation = simulatorView.getFirstFreeLocation();
        if (freeLocation != null) {
            simulatorView.setCarAt(freeLocation, car);
            int stayMinutes = (int) (15 + random.nextFloat() * 10 * 60);
            car.setMinutesLeft(stayMinutes);
        }
    }

    // Perform car park tick.
    simulatorView.tick();

    // Add leaving cars to the exit queue.
    while (true) {
        Car car = simulatorView.getFirstLeavingCar();
        if (car == null) {
            break;
        }
        car.setIsPaying(true);
        paymentCarQueue.addCar(car);
    }

    // Let cars pay.
    for (int i = 0; i < paymentSpeed; i++) {
        Car car = paymentCarQueue.removeCar();
        if (car == null) {
            break;
        }
        // TODO Handle payment.
        simulatorView.removeCarAt(car.getLocation());
        exitCarQueue.addCar(car);
    }

    // Let cars leave.
    for (int i = 0; i < exitSpeed; i++) {
        Car car = exitCarQueue.removeCar();
        if (car == null) {
            break;
        }
        // Bye!
    }

    // Update the car park view.

    simulatorView.updateView();

    // Pause.
    try {
        Thread.sleep(tickPause);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}


@Override
public void actionPerformed(ActionEvent e)
{
    String command = e.getActionCommand();
    switch (command) {
        case "oneStep":
            run(1);
            break;
        case "hundredSteps":
            run(10);
            break;
        default:
            break;
    }

}


public static void main (String[] args) {
    Simulator start = new Simulator();
}


}
Location.java:

public class Location {

private int floor;
private int row;
private int place;

/**
 * Constructor for objects of class Location
 */
public Location(int floor, int row, int place) {
    this.floor = floor;
    this.row = row;
    this.place = place;
}

/**
 * Implement content equality.
 */
public boolean equals(Object obj) {
    if(obj instanceof Location) {
        Location other = (Location) obj;
        return floor == other.getFloor() && row == other.getRow() && place == other.getPlace();
    }
    else {
        return false;
    }
}

/**
 * Return a string of the form floor,row,place.
 * @return A string representation of the location.
 */
public String toString() {
    return floor + "," + row + "," + place;
}

/**
 * Use the 10 bits for each of the floor, row and place
 * values. Except for very big car parks, this should give
 * a unique hash code for each (floor, row, place) tupel.
 * @return A hashcode for the location.
 */
public int hashCode() {
    return (floor << 20) + (row << 10) + place;
}

/**
 * @return The floor.
 */
public int getFloor() {
    return floor;
}

/**
 * @return The row.
 */
public int getRow() {
    return row;
}

/**
 * @return The place.
 */
public int getPlace() {
    return place;
}

}
当您从actionListener调用run()时,它将在EventDispatchTread上执行,阻塞该线程,直到run()方法终止。只有在这一点之后,EDT才能对重绘请求进行操作。

当您从actionListener调用run()时,它将在EventDispatchTread上执行,阻塞该线程,直到run()方法终止。只有在这一点之后,EDT才能对重新绘制请求进行操作。

“通常,Swing不是线程安全的。除非另有说明,否则所有Swing组件和相关类都必须在事件调度线程上访问。”

因此,当执行操作时,
actionListener
拾取该事件并在事件调度线程中执行
run()

在步骤0,当涉及到
repaint()
时,它不会绘制任何内容,因为“在调度所有当前挂起的事件之后,将重新绘制组件。”

repaint()
正在等待发布
ActionListener.actionPerformed

到了第1步,同样的事情发生了。只有在最后,在动作事件被释放后,
repaint()
才会执行一次。所以这就是为什么只有结果,中间没有步骤

我的建议是将绘制逻辑放在主线程中,而不是
actionListener

“通常,Swing不是线程安全的。除非另有说明,否则必须在事件调度线程上访问所有Swing组件和相关类。”

因此,当执行操作时,
actionListener
拾取该事件并在事件调度线程中执行
run()

在步骤0,当涉及到
repaint()
时,它不会绘制任何内容,因为“在调度所有当前挂起的事件之后,将重新绘制组件。”

repaint()
正在等待发布
ActionListener.actionPerformed

到了第1步,同样的事情发生了。只有在最后,在动作事件被释放后,
repaint()
才会执行一次。所以这就是为什么只有结果,中间没有步骤


我的建议是将绘制逻辑放在主线程中,而不是
actionListener

谢谢@FredK&Max,通过在actionPerformed函数中创建一个新线程来修复它。谢谢@FredK&Max,通过在actionPerformed函数中创建一个新线程来修复它。
public abstract class Car {

private
Location location;
private int minutesLeft;
private boolean isPaying;

/**
 * Constructor for objects of class Car
 */
public Car() {

}

public Location getLocation() {
    return location;
}

public void setLocation(Location location) {
    this.location = location;
}

public int getMinutesLeft() {
    return minutesLeft;
}

public void setMinutesLeft(int minutesLeft) {
    this.minutesLeft = minutesLeft;
}

public boolean getIsPaying() {
    return isPaying;
}

public void setIsPaying(boolean isPaying) {
    this.isPaying = isPaying;
}

public void tick() {
    System.out.println("car-tick");
    minutesLeft--;
}

}
public class Location {

private int floor;
private int row;
private int place;

/**
 * Constructor for objects of class Location
 */
public Location(int floor, int row, int place) {
    this.floor = floor;
    this.row = row;
    this.place = place;
}

/**
 * Implement content equality.
 */
public boolean equals(Object obj) {
    if(obj instanceof Location) {
        Location other = (Location) obj;
        return floor == other.getFloor() && row == other.getRow() && place == other.getPlace();
    }
    else {
        return false;
    }
}

/**
 * Return a string of the form floor,row,place.
 * @return A string representation of the location.
 */
public String toString() {
    return floor + "," + row + "," + place;
}

/**
 * Use the 10 bits for each of the floor, row and place
 * values. Except for very big car parks, this should give
 * a unique hash code for each (floor, row, place) tupel.
 * @return A hashcode for the location.
 */
public int hashCode() {
    return (floor << 20) + (row << 10) + place;
}

/**
 * @return The floor.
 */
public int getFloor() {
    return floor;
}

/**
 * @return The row.
 */
public int getRow() {
    return row;
}

/**
 * @return The place.
 */
public int getPlace() {
    return place;
}

}
import java.util.LinkedList;
import java.util.Queue;

public class CarQueue {
private Queue<Car> queue = new LinkedList<>();

public boolean addCar(Car car) {
    return queue.add(car);
}

public Car removeCar() {
    return queue.poll();
}

}
public class AdHocCar extends Car {
public AdHocCar() {

}
}