我必须在学校做作业,这是一个停车场模拟器,调用run();从main开始,给出了一个完全正常的模拟,其中每个步骤都重新绘制了carpots,但是当从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();
}
}
SimulatorView.java:
import javax.swing.*;
import java.awt.*;
public class SimulatorView extends JFrame {
private CarParkView carParkView;
private int numberOfFloors;
private int numberOfRows;
private int numberOfPlaces;
private Car[][][] cars;
public SimulatorView(int numberOfFloors, int numberOfRows, int numberOfPlaces, Simulator parent) {
this.numberOfFloors = numberOfFloors;
this.numberOfRows = numberOfRows;
this.numberOfPlaces = numberOfPlaces;
cars = new Car[numberOfFloors][numberOfRows][numberOfPlaces];
carParkView = new CarParkView();
Container contentPane = getContentPane();
//contentPane.add(stepLabel, BorderLayout.NORTH);
contentPane.add(carParkView, BorderLayout.CENTER);
//contentPane.add(population, BorderLayout.SOUTH);
JButton stepForward = new JButton("oneStep");
stepForward.addActionListener(parent);
JButton stepHundredForward = new JButton("hundredSteps");
stepHundredForward.addActionListener(parent);
JMenuBar stepBar = new JMenuBar();
stepBar.add(stepForward);
stepBar.add(stepHundredForward);
contentPane.add(stepBar, BorderLayout.SOUTH);
pack();
setVisible(true);
updateView();
}
public void updateView() {
carParkView.updateView();
}
public int getNumberOfFloors() {
return numberOfFloors;
}
public int getNumberOfRows() {
return numberOfRows;
}
public int getNumberOfPlaces() {
return numberOfPlaces;
}
public Car getCarAt(Location location) {
if (!locationIsValid(location)) {
return null;
}
return cars[location.getFloor()][location.getRow()][location.getPlace()];
}
public boolean setCarAt(Location location, Car car) {
if (!locationIsValid(location)) {
return false;
}
Car oldCar = getCarAt(location);
if (oldCar == null) {
cars[location.getFloor()][location.getRow()][location.getPlace()] = car;
car.setLocation(location);
return true;
}
return false;
}
public Car removeCarAt(Location location) {
if (!locationIsValid(location)) {
return null;
}
Car car = getCarAt(location);
if (car == null) {
return null;
}
cars[location.getFloor()][location.getRow()][location.getPlace()] = null;
car.setLocation(null);
return car;
}
public Location getFirstFreeLocation() {
for (int floor = 0; floor < getNumberOfFloors(); floor++) {
for (int row = 0; row < getNumberOfRows(); row++) {
for (int place = 0; place < getNumberOfPlaces(); place++) {
Location location = new Location(floor, row, place);
if (getCarAt(location) == null) {
return location;
}
}
}
}
return null;
}
public Car getFirstLeavingCar() {
for (int floor = 0; floor < getNumberOfFloors(); floor++) {
for (int row = 0; row < getNumberOfRows(); row++) {
for (int place = 0; place < getNumberOfPlaces(); place++) {
Location location = new Location(floor, row, place);
Car car = getCarAt(location);
if (car != null && car.getMinutesLeft() <= 0 && !car.getIsPaying()) {
return car;
}
}
}
}
return null;
}
public void tick() {
System.out.println("simulatorview-tick");
for (int floor = 0; floor < getNumberOfFloors(); floor++) {
for (int row = 0; row < getNumberOfRows(); row++) {
for (int place = 0; place < getNumberOfPlaces(); place++) {
Location location = new Location(floor, row, place);
Car car = getCarAt(location);
if (car != null) {
car.tick();
}
}
}
}
}
private boolean locationIsValid(Location location) {
int floor = location.getFloor();
int row = location.getRow();
int place = location.getPlace();
if (floor < 0 || floor >= numberOfFloors || row < 0 || row > numberOfRows || place < 0 || place > numberOfPlaces) {
return false;
}
return true;
}
private class CarParkView extends JPanel {
private Dimension size;
private Image carParkImage;
/**
* Constructor for objects of class CarPark
*/
public CarParkView() {
size = new Dimension(0, 0);
}
/**
* Overridden. Tell the GUI manager how big we would like to be.
*/
public Dimension getPreferredSize() {
return new Dimension(800, 500);
}
/**
* Overriden. The car park view component needs to be redisplayed. Copy the
* internal image to screen.
*/
public void paintComponent(Graphics g) {
if (carParkImage == null) {
return;
}
Dimension currentSize = getSize();
if (size.equals(currentSize)) {
g.drawImage(carParkImage, 0, 0, null);
}
else {
// Rescale the previous image.
g.drawImage(carParkImage, 0, 0, currentSize.width, currentSize.height, null);
}
}
public void updateView() {
// Create a new car park image if the size has changed.
if (!size.equals(getSize())) {
size = getSize();
carParkImage = createImage(size.width, size.height);
}
Graphics graphics = carParkImage.getGraphics();
for(int floor = 0; floor < getNumberOfFloors(); floor++) {
for(int row = 0; row < getNumberOfRows(); row++) {
for(int place = 0; place < getNumberOfPlaces(); place++) {
Location location = new Location(floor, row, place);
Car car = getCarAt(location);
Color color = car == null ? Color.white : Color.red;
drawPlace(graphics, location, color);
}
}
}
repaint();
}
/**
* Paint a place on this car park view in a given color.
*/
private void drawPlace(Graphics graphics, Location location, Color color) {
graphics.setColor(color);
graphics.fillRect(
location.getFloor() * 260 + (1 + (int)Math.floor(location.getRow() * 0.5)) * 75 + (location.getRow() % 2) * 20,
60 + location.getPlace() * 10,
20 - 1,
10 - 1); // TODO use dynamic size or constants
}
}
}
Car.java:
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--;
}
}
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;
}
}
CarQueue.java:
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();
}
}
AdHocCar.java:
public class AdHocCar extends Car {
public AdHocCar() {
}
}
答案 0 :(得分:0)
当你从actionListener调用run()时,它正在EventDispatchTread上执行,阻塞该线程,直到run()方法终止。只有在那之后,EDT才会对重绘请求采取行动。
答案 1 :(得分:0)
“一般情况下,Swing不是线程安全的。除非另有说明,否则必须在事件派发线程上访问所有Swing组件和相关类。” see here
因此,当执行某个操作时,actionListener
会选择该事件并在事件调度线程中执行run()
。
在步骤0,当涉及repaint()
时,它不会绘制任何内容,因为“在调度了所有当前挂起的事件之后,将重新绘制该组件。” see here
repaint()
正在等待ActionListener.actionPerformed
的发布。
所以说到第1步,同样的事情发生了。仅在最后,在动作事件发布后,repaint()
将执行一次。所以这就是为什么只有结果没有介于两者之间的步骤。
我的建议是将绘画逻辑放在主线程而不是actionListener
。