我正在開發一個測量由 Car 物件驅動的“距離”的應用程式。每輛車都是它自己的執行緒,有一個隨機生成的速度,我計算每輛車行駛的距離。雖然我能夠以我想要的任何時間間隔從執行緒內獲取正確的資料以列印到控制臺(當前一次/秒,但以后可能會更新),但我正在嘗試找出一種方法來代替顯示該資訊一個擺動 GUI(作為表格行,或者只是作為文本區域行)由于 Car 類是一個單獨的檔案,我不能讓它直接推送到 GUI(或者至少,我不知道該怎么做那)。我需要定期更新 2 個“列”資訊:當前速度和距離。
我嘗試做的是:設定一個 jtable 并提取行資料:這使我可以捕獲快照,但不能不斷更新資料。
將資訊推送到 jtextarea:我可以將其作為快照。當我嘗試將其包裝在一個while回圈中(執行緒運行時,追加...)時,系統崩潰了。當我嘗試將追加包裝在 Thread.sleep 中時,結果相同。
因為在任何給定時間我都可以拍攝汽車位置的快照(car.getLocation()),所以我在想也許主要方法可以每秒主動尋找該快照,但是,當我嘗試使用 while 回圈時和一個 Thread.sleep,如前所述,它使系統崩潰。
另外值得注意的是,當完成時,GUI 將允許創建任意數量的 Car 物件,并且我希望每個物件都有一行定期更新,以便可以比較距離數。
編輯:根據@matt 的建議,我添加了一個搖擺計時器并修改了 GUI 以適應。現在的挑戰是,當我調整頁面大小時,新的 jtextfield 只會在顯示屏上彈出。有沒有辦法以某種方式更新/重繪 GUI?
更新的 GUI 元素:
JButton carb = new JButton("add car");
GridBagConstraints carC = new GridBagConstraints();
carC.gridx = 1;
carC.gridy = 2;
carb.addActionListener(a -> {
totalCars ;
String carName = "Car" totalCars;
Car car = new Car(carName);
cars.add(car);
Thread thread = new Thread(car);
JTextField t = new JTextField(50);
GridBagConstraints tC = new GridBagConstraints();
t.setEditable(false);
tC.gridx = 1;
tC.gridy = currentcol;
currentcol ;
content.add(t, tC);
running = true;
thread.start();
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
t.setText(df.format(car.getLocation()));
t.setText("Name: " carName ", Drive speed: " car.getSpeed() ", Current speed: "
car.getCurrentSpeed() ", Location (distance from beginning): "
df.format(car.getLocation()));
}
};
Timer timer = new Timer(1000, taskPerformer);
timer.start();
});
content.add(carb, carC);
代碼(到目前為止):汽車類:
private void setLocation(long timeElapsed) {
location = location ((timeElapsed-lastCheck)*(currentSpeed*0.44704));
lastCheck = timeElapsed;
}
public void run() {
long startTime = System.currentTimeMillis();
long elapsedTime;
while (flag) {
try {
Thread.sleep(1000);
elapsedTime = System.currentTimeMillis()-startTime;
elapsedTime = elapsedTime/1000;
setLocation(elapsedTime);
System.out.println(getName() ": " df.format(getLocation()) " meters");
} catch (InterruptedException e) {
}
}
}
這是我嘗試使用 main 方法中的 Thread.sleep 的方法。這不起作用(使系統崩潰):
while (running) {
try {
Thread.sleep(1000);
carArea.append("\nName: " carName ", Drive speed: " car.getSpeed() ", Current speed: "
car.getCurrentSpeed() ", Location (distance from beginning): " car.getLocation());
} catch (InterruptedException e) {
}
}
uj5u.com熱心網友回復:
首先,首先看一下
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private List<Car> cars = new ArrayList<>(32);
private JTextArea ta = new JTextArea(10, 20);
private Random rnd = new Random();
private NumberFormat format = NumberFormat.getNumberInstance();
public TestPane() {
for (int index = 0; index < 32; index ) {
Car car = new Car("Car " index, 40.0 (rnd.nextDouble() * 180.0));
cars.add(car);
}
setLayout(new BorderLayout());
add(new JScrollPane(ta));
Timer timer = new Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for (Car car : cars) {
ta.append(car.getName() " @ " format.format(car.getSpeedKPH()) " ~ " format.format(car.getLocation()) "km\n");
if (!car.isRunning() && rnd.nextBoolean()) {
car.start();
ta.append(car.getName() " got started\n");
}
}
}
});
timer.start();
}
}
public class Car {
private String name;
private double speedKPH;
private Instant timeStartedAt;
public Car(String name, double kmp) {
this.speedKPH = kmp;
this.name = name;
}
public String getName() {
return name;
}
public double getSpeedKPH() {
return speedKPH;
}
public Instant getTimeStartedAt() {
return timeStartedAt;
}
public boolean isRunning() {
return timeStartedAt != null;
}
public void start() {
timeStartedAt = Instant.now();
}
protected double distanceTravelledByMillis(long millis) {
double time = millis / 1000d / 60d / 60d;
return getSpeedKPH() * time;
}
public double getLocation() {
Instant timeStartedAt = getTimeStartedAt();
if (timeStartedAt == null) {
return 0;
}
Duration time = Duration.between(timeStartedAt, Instant.now());
return distanceTravelledByMillis(time.toMillis());
}
}
}
您還可以控制更新的速度,這將允許您在看到問題之前將解決方案擴展到更多的汽車(數萬輛)(盡管JTextArea在那之前會是瓶頸)
按 ( EventQueue.invokeLater)
這是一個更隨機的例子,每輛車都有自己的更新間隔,通過觀察者觸發回呼。然后,觀察者需要將呼叫同步回事件調度佇列,然后才能將更新添加到文本區域。
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.text.NumberFormat;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private List<Car> cars = new ArrayList<>(32);
private JTextArea ta = new JTextArea(10, 20);
private Random rnd = new Random();
private NumberFormat format = NumberFormat.getNumberInstance();
public TestPane() {
for (int index = 0; index < 32; index ) {
int timeInterval = 500 rnd.nextInt(4500);
Car car = new Car("Car " index, 40.0 (rnd.nextDouble() * 180.0), timeInterval, new Car.Observer() {
@Override
public void didChangeCar(Car car) {
updateCar(car);
}
});
cars.add(car);
car.start();
}
setLayout(new BorderLayout());
add(new JScrollPane(ta));
}
protected void updateCar(Car car) {
if (!EventQueue.isDispatchThread()) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
updateCar(car);
}
});
}
ta.append(car.getName() " @ " format.format(car.getSpeedKPH()) " ~ " format.format(car.getLocation()) "km\n");
}
}
public class Car {
public interface Observer {
public void didChangeCar(Car car);
}
private String name;
private double speedKPH;
private Instant timeStartedAt;
private int notifyInterval;
private Observer observer;
private Thread thread;
public Car(String name, double kmp, int notifyInterval, Observer observer) {
this.speedKPH = kmp;
this.name = name;
this.notifyInterval = notifyInterval;
this.observer = observer;
}
public String getName() {
return name;
}
public double getSpeedKPH() {
return speedKPH;
}
public Instant getTimeStartedAt() {
return timeStartedAt;
}
public boolean isRunning() {
return timeStartedAt != null;
}
public void start() {
if (thread != null) {
return;
}
timeStartedAt = Instant.now();
thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(notifyInterval);
observer.didChangeCar(Car.this);
} catch (InterruptedException ex) {
}
}
});
thread.start();
}
protected double distanceTravelledByMillis(long millis) {
double time = millis / 1000d / 60d / 60d;
return getSpeedKPH() * time;
}
public double getLocation() {
Instant timeStartedAt = getTimeStartedAt();
if (timeStartedAt == null) {
return 0;
}
Duration time = Duration.between(timeStartedAt, Instant.now());
return distanceTravelledByMillis(time.toMillis());
}
}
}
這會給 EDT 帶來大量開銷,因為每輛汽車都需要在 EDT 上添加一個請求以進行處理,因此您添加的汽車越多,EDT 的速度就越慢。
uj5u.com熱心網友回復:
考慮使用MCV 模型構建您的代碼。這將模型、視圖和控制器之間的職責分開。
每一個(M、V 和 C)都成為定義明確的單一職責類。起初,類的數量以及它們之間的關系可能看起來令人費解。
在研究并理解了結構之后,您意識到它實際上將手頭的任務劃分為更小且更易于處理的部分。
我的答案(和代碼)基于 MadProgramer綜合答案中的“推送”選項:
import java.awt.*;
import java.text.*;
import java.time.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
public class Main {
public static void main(String[] args) {
new CarsControler().startCars();
}
}
class CarsControler implements Observer{
private final Cars cars;
private final CarGui gui;
private final NumberFormat format = NumberFormat.getNumberInstance();
private boolean stopCars = false;
public CarsControler() {
cars = new Cars(32);
gui = new CarGui();
}
public void startCars(){
Random rnd = new Random();
gui.update("Strarting cars\n");
for(Car car : cars.getCars()){
if (! car.isRunning()) {
car.start();
}
final int moveInterval = 2000 rnd.nextInt(8000);
Thread thread = new Thread(() -> {
try {
while(! stopCars){
Thread.sleep(moveInterval);
carChanged(car);
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
});
thread.start();
}
}
public void setStopCars(boolean stopCars) {
this.stopCars = stopCars;
}
@Override
public void carChanged(Car car) {
String message = car.getName() " @ " format.format(car.getSpeedKPH()) " ~ " format.format(car.getLocation()) "km\n";
gui.update(message);
}
}
/**
* Model
* TODO: add thread safety if model is used by multiple threads.
*/
class Cars {
private final List<Car> cars;
private final Random rnd = new Random();
public Cars(int numberOfCars) {
cars = new ArrayList<>(numberOfCars);
for (int index = 0; index < 32; index ) {
Car car = new Car("Car " index, 40.0 rnd.nextDouble() * 180.0);
cars.add(car);
}
}
//returns a defensive copy of cars
public List<Car> getCars() {
return new ArrayList<>(cars);
}
}
class CarGui{
private final JTextArea ta = new JTextArea(10, 20);
public CarGui() {
JFrame frame = new JFrame();
JPanel testPane = new JPanel(new BorderLayout());
testPane.add(new JScrollPane(ta));
frame.add(testPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
void update(String s) {
if (!EventQueue.isDispatchThread()) {
EventQueue.invokeLater(() -> update(s));
} else {
ta.append(s);
}
}
}
class Car {
private final String name;
private final double speedKPH;
private Instant timeStartedAt;
public Car(String name, double speedKPH) {
this.speedKPH = speedKPH;
this.name = name;
}
public String getName() {
return name;
}
public double getSpeedKPH() {
return speedKPH;
}
public Instant getTimeStartedAt() {
return timeStartedAt;
}
public boolean isRunning() {
return timeStartedAt != null;
}
public void start() {
timeStartedAt = Instant.now();
}
protected double distanceTravelledByMillis(long millis) {
double time = millis / 1000d / 60d / 60d;
return getSpeedKPH() * time;
}
public double getLocation() {
Instant timeStartedAt = getTimeStartedAt();
if (timeStartedAt == null) return 0;
Duration time = Duration.between(timeStartedAt, Instant.now());
return distanceTravelledByMillis(time.toMillis());
}
}
interface Observer {
void carChanged(Car car);
}
uj5u.com熱心網友回復:
謝謝大家!考慮到您所說的一切,我決定嘗試使用擺動計時器并提取資料。除了content.revalidate();content.repaint();在添加我想要的元素后添加解決的重繪 問題之外,這有效。再次感謝!
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/462464.html
