我正在寻求有关程序设计的一些常规建议。我已经对此发表了意见,但是这个问题由于不清楚而被搁置了,所以我再说一遍。我有一个摆动的GUI,但在后台正在进行大量处理,基本上是在更新我拥有的对象的数组列表。根据对我以前的帖子的有帮助的回复,我得到了类似的东西:
class GUI extends JFrame {
menu, various displays, buttons…
public GUI(CompletableFuture<GUI> result) {
this.add(jButton, null);
…
result.complete(this);
}
public void updateGraphics(MyObj o) {
update graphics with updated object…
}
}
public static void main (String[]args) throws Exception {
ArrayList<MyObject> MyList = new ArrayList<>();
CompletableFuture<GUI> result = new CompletableFuture();
EventQueue.invokeLater(() -> new GUI(result));
GUI gui = result.get();
// main processing, can go on for hours
// continually updating arraylist of MyObjects
// pass computed values to GUI as they are updated
EventQueue.invokeLater(() -> gui.updateGraphics (MyList.get(1)));
}
我希望这是有道理的。我的问题是,GUI如何与arraylist交互?例如,可能有一个删除第三个MyObject的按钮。我是否需要在开始时传递对arraylist的引用,而不是:
EventQueue.invokeLater(() -> new GUI(result));
应该是:
EventQueue.invokeLater(() -> new GUI(result, MyList));
因此,GUI可以从MyList中删除... ???也许GUI需要在main中调用在主线程上处理的方法?
预先感谢您的帮助
答案 0 :(得分:0)
首先,删除所有CompleteableFuture代码。
首先,您想在EventDispatch线程上显示GUI,即JFrame。所以,
Runnable runnable = new Runnable() {
public void run() {
new GUI().setVisible(true);
}
}
EventQueue.invokeLater(runnable);
这将导致您的JFrame显示。
您的myList是main()方法中的局部变量。而是创建一个模型类来保存您的变量。对于简单的应用程序,您无需担心同步,只要您在事件分发线程上同步完成所有工作即可,这是默认行为。
最后,您需要一个将类和GUI类联系在一起的控制器类。此类将侦听器添加到GUI类,并处理逻辑以更新模型并将模型中的值传递回GUI类。
一旦您有了基本的MVC类并且您的应用程序正常运行,您就可以使用线程从EDT上卸载工作。除非完成工作需要“很长时间”,否则这不是必不可少的,在这种情况下,您的UI会变得呆滞。
这里的关键是在完成工作之后,向EventQueue.invokeLater()提供另一个Runnable来更新UI。这样可以确保UI中完成的所有工作都由一个线程完成。
答案 1 :(得分:0)
以下代码实现了Model-View-Controller模式。它是一个文件SSCCE:可以复制粘贴到MVC.java
中并运行
看法就是这样。它使用Observer
界面侦听Model中的更改。
该模型封装视图所需的信息(在这种情况下为ArrayList
),并在信息更改时通知它。
Worker
类使用线程来更改Model
中的信息。
Controller
协调各种成员:初始化它们,并将视图链接到模型:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class MVC {
public static void main(String[] args) {
new Controller();
}
}
//Controller of the MVC pattern."wires" model and view (and in this case also worker)
class Controller{
public Controller() {
Model model = new Model();
View view = new View(model);
model.registerObserver(view); //register view as an observer to model
intitialize(model);
Worker worker = new Worker(model);
view.getClearBtn().addActionListener(e -> model.fill(0.));
view.getStopBtn().addActionListener(e -> worker.cancel());
}
//add some initial values to model
private void intitialize(Model model) {
IntStream.range(0, 1000).forEach(i-> model.addElement(0));
}
}
//view of the MVC pattern. Implements observer to respond to model changes
class View implements Observer{
private final Model model;
private final SumPane pane;
private final JButton clearBtn, stopBtn;
public View(Model model) {
this.model = model;
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//used to simulate doing some changes in the array list
clearBtn = new JButton("Set Values to 0");
frame.add(clearBtn, BorderLayout.NORTH);
pane = new SumPane();
frame.add(pane, BorderLayout.CENTER);
stopBtn = new JButton("Stop");
frame.add(stopBtn, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
}
JButton getClearBtn() { return clearBtn;}
JButton getStopBtn() { return stopBtn; }
@Override
public void onObservableChanged() { //update text in response to change in model
pane.setText(String.format("%.2f",model.sum()));
}
class SumPane extends JPanel {
private final JLabel label;
SumPane() {
setPreferredSize(new Dimension(200, 100));
setLayout(new GridBagLayout());
label = new JLabel(" ");
add(label);
}
void setText(String text){ label.setText(text); }
}
}
//Model of the MVC pattern. Holds the information view needs
//Notifies observers (in this case View) when model changes
class Model { //you can make it generic Model<T>
//Collection of the info that need to be processed
private final List<Double> list;
// thread safe set for observers
private final Set<Observer> mObservers = Collections.newSetFromMap(
new ConcurrentHashMap<Observer, Boolean>(0));
Model() {
//thread safe list. alternatively use FXCollections.observableArrayList
//and synchronize
list = new CopyOnWriteArrayList<>();
}
Iterator<Double> getIterator(){
return list.iterator();
}
//set all elements to value
void fill(Double value){
IntStream.range(0, getLength()).forEach(i ->setElement(i,value));
notifyObservers();
}
//return sum of all values
double sum(){
return list.stream().mapToDouble(d -> d.doubleValue()).sum();
}
int getLength(){return list.size(); }
//May throw IndexOutOfBoundsException
Double getElement(int index){
Double element = list.get(index);
notifyObservers();
return element;
}
void setElement(int index, double element){
list.set(index, element);
notifyObservers();
}
void addElement(double element){
list.add(element);
notifyObservers();
}
//Returns:the element previously at index.
//May throw IndexOutOfBoundsException
Double removeElement(int index){
Double element = list.remove(index);
notifyObservers();
return element;
}
//-- handle observers
// add new Observer - it will be notified when Observable changes
public void registerObserver(Observer observer) {
if (observer != null) {
mObservers.add(observer);
}
}
//remove an Observer
public void unregisterObserver(Observer observer) {
if (observer != null) {
mObservers.remove(observer);
}
}
//notifies registered observers
private void notifyObservers() {
for (Observer observer : mObservers) {
observer.onObservableChanged();
}
}
}
//Interface implemented by View and used by Model
interface Observer {
void onObservableChanged();
}
//Encapsulates thread that does some work on model
class Worker implements Runnable{
private final Model model;
private boolean cancel = false;
private final Random rnd = new Random();
public Worker(Model model) {
this.model = model;
new Thread(this).start();
}
@Override
public void run() {
while(! cancel){
model.fill(rnd.nextDouble()* Math.PI);
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException ex) { ex.printStackTrace(); }
}
}
void cancel() { cancel = true; }
}