Java,程序最终只重绘()

时间:2016-04-01 14:46:57

标签: java repaint

我必须在学校做作业,这是一个停车场模拟器,调用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() {

}
}

2 个答案:

答案 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