【发布时间】:2018-10-11 14:09:47
【问题描述】:
我的 Java JFrame 项目遇到了一个繁琐的问题。
我想要做的(并寻找如何做)是在不冻结我的应用程序的情况下,从一个无 GUI 的类中实时添加元素到我的 ListBox,或者换句话说“异步”。这清楚吗?我尝试了SwingWorker 和线程但没有结果。我所能做的就是在所有进程完成后更新列表框(显然我的应用程序冻结了,因为我的进程很长)。
这是我的架构:
这是我的代码(不是功能性的,只是为了理解)
已编辑
视图(使用 NetBeans 生成)
package view;
import com.everis.ingesta.controller.MyController;
import java.awt.event.ActionListener;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
public class MyView extends javax.swing.JFrame {
public MyView(DefaultListModel<String> model) {
setVisible(true);
}
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
btnRun = new javax.swing.JButton();
jscrlLog = new javax.swing.JScrollPane();
jlstLog = new javax.swing.JList();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
btnRun.setText("Run");
jscrlLog.setViewportView(jlstLog);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(159, 159, 159)
.addComponent(btnRun)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jscrlLog, javax.swing.GroupLayout.DEFAULT_SIZE, 376, Short.MAX_VALUE)
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(btnRun)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jscrlLog, javax.swing.GroupLayout.DEFAULT_SIZE, 242, Short.MAX_VALUE)
.addContainerGap())
);
pack();
}// </editor-fold>
public void addButtonListener(ActionListener listener) {
btnRun.addActionListener(listener);
}
public static void main(String args[]) {
/* Set the Nimbus look and feel */
//<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
/* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
* For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
*/
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(MyView.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(MyView.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(MyView.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(MyView.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
//</editor-fold>
//</editor-fold>
//</editor-fold>
//</editor-fold>
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new MyController();
}
});
}
// Variables declaration - do not modify
private javax.swing.JButton btnRun;
private javax.swing.JList jlstLog;
private javax.swing.JScrollPane jscrlLog;
// End of variables declaration
}
控制器
package controller;
import business.MyBusiness;
import util.MyLog;
import view.MyView;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class MyController {
MyLog log;
MyBusiness business;
MyView view;
public MyController(){
log = new MyLog();
business = new MyBusiness(log.getLog());
view = new MyView(log.getLog());
}
public void runProcess() {
view.addButtonListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
business.runProcess();
}}
);
}
}
商业
package business;
import MyLog;
import java.util.List;
import javax.swing.DefaultListModel;
import javax.swing.SwingWorker;
public class MyBusiness {
private int counter = 0;
private DefaultListModel<String> model;
private MyLog log;
public MyBusiness(DefaultListModel<String> model) {
this.model = model;a
}
public void runProcess() {
SwingWorker<Void, String> worker = new SwingWorker<Void, String>() {
@Override
protected Void doInBackground() throws Exception {
for (int i = 0; i < 10; i++) {
publish("log message number " + counter++);
Thread.sleep(2000);
}
return null;
}
@Override
protected void process(List<String> chunks) {
// this is called on the Swing event thread
for (String text : chunks) {
model.addElement("");
}
}
};
worker.execute();
}
}
日志(模型)
package util;
import javax.swing.DefaultListModel;
public class MyLog {
private DefaultListModel<String> model;
public MyLog() {
model = new DefaultListModel<String>();
}
public DefaultListModel<String> getLog(){
return model;
}
}
【问题讨论】:
-
"I tried SwingWorker and Threads..."-- 实际上 是 正确的解决方案。"... but without results. All I can do is update the listbox after all process finish"——这意味着你做错了什么,你的代码中有一个错误,很可能你的代码没有适当地遵守 Swing 线程规则。让我们充分了解您的问题的最佳方法是,如果我们能够重现您的问题,并提供有效的minimal reproducible example 代码帖子,其中包括您的 SwingWorker 尝试。 -
我已经编辑了你的标签——最好使用更具体的swing标签,而不是过于宽泛的user-interface标签。
-
所以我现在可以给你的唯一答案是——是的,使用你提到的 SwingWorker,并努力在其
doInBackground方法中进行所有长时间运行的调用,并且没有您从该方法调用的 Swing(或从该方法调用的代码)。这将解决您的问题。如果您需要更具体的解决方案,请阅读上述minimal reproducible example 链接。 -
您希望将日志添加到
JList吗? -
如果是这样,如果您尝试执行@c0der 上面要求的操作,那么您可能需要使用 SwingWorker 的发布/处理方法对来更新列表的模型。 再次,看到您的 mcve 将非常有帮助,其中包括您的 SwingWorker 实现。
标签: java swing asynchronous model-view-controller jframe