动态地向JTable添加行 – 为什么它们一次出现?

在这个例子中,我试图在我的GUI中添加一个表,然后动态地向其添加行(以显示进度)。 我不明白的是为什么所有的行都会立刻出现。 我的意思是,桌子的变化,不是吗? 有人可以给我一个解释吗?

import java.awt.Component; public class Main { public static void main(String[] args) { // Show GUI java.awt.EventQueue.invokeLater(new Runnable() { public void run() { GUI gui = new GUI(); gui.setVisible(true); DefaultTableModel model = new DefaultTableModel( new String[] { "Column 1", "Column 2" }, 0); JTable table = new JTable(model); gui.add(table); gui.validate(); for (int i = 0; i < 10; i++) { System.out.println("Row " + i); model .addRow(new String[] { "Row", String.valueOf(i) }); // model.fireTableDataChanged(); try { Thread.sleep(250); } catch (InterruptedException e) { e.printStackTrace(); } } } }); } } class GUI extends JFrame { private static final long serialVersionUID = 1L; public GUI() { setTitle("GUI"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(100, 100, 350, 100); setLocationRelativeTo(null); JPanel cp = new JPanel(); cp.setBorder(new EmptyBorder(10, 10, 10, 10)); setContentPane(cp); } } 

重申Kleopatra: 不要睡觉EDT

您可以改为使用javax.swing.Timer如本答案所示

在此处输入图像描述


编辑

我不想乱用你的代码(只是因为它看起来很奇怪)但我改了一下就添加了Timer

 import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.Timer; import javax.swing.border.EmptyBorder; import javax.swing.table.DefaultTableModel; public class Main { static JTable table; static GUI gui; static Processor p = null; public static void main(String[] args) { // Show GUI java.awt.EventQueue.invokeLater(new Runnable() { public void run() { gui = new GUI(); p = new Processor() { @Override public void execute() { final JTable table = new JTable(p.getTableModel()); final JScrollPane scrollPane = new JScrollPane(table); gui.getContentPane().add(scrollPane, BorderLayout.CENTER); gui.setLocationRelativeTo(null); gui.setVisible(true); Timer timer = new Timer(100, new ActionListener(){ public void actionPerformed(ActionEvent e) { p.processRow(); table.scrollRectToVisible(table.getCellRect(table.getRowCount() - 1, 0, true)); } }); timer.start(); } }; p.execute(); } }); } } class GUI extends JFrame { private static final long serialVersionUID = 1L; public GUI() { setTitle("GUI"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(100, 100, 350, 400); JPanel contentPane = new JPanel(); contentPane.setBorder(new EmptyBorder(10, 10, 10, 10)); contentPane.setLayout(new BorderLayout(0, 0)); setContentPane(contentPane); } } interface Callback { void execute(); } abstract class Processor implements Callback { private final String[] cols = {"COL", "COL", "COL", "COL", "COL"}; private DefaultTableModel tableModel; int numRows; int numCols; int a, b, c, d, e; Processor() { a = 1; b = 2; c = 3; d = 4; e = 4; numRows = 1000; tableModel = new DefaultTableModel(cols, numCols); } public DefaultTableModel getTableModel() { return tableModel; } public void processRow() { tableModel.addRow(new Object[]{a, b, c, d, e}); a++; b++; c++; d++; e++; } } 

正如kleopatra和peeskillet指出的那样,我最初的例子遭遇了一个愚蠢的错误。 值得注意的是,peeskillet和我正在采用不同的方法。 在我的示例中,列旨在表示可能花费未知时间且实际上可能失败的连接尝试(或多或少)(在这种情况下,只有在这种情况下,下一列才会起作用等等) )。 因此,对我来说一次添加行是没有意义的(这可能是因为我的例子对peeskillet来说看起来很奇怪)。 我已经使用SwingWorker解决了这个任务。 正如kleopatra所指出的,还有另一个错误,现在已经修复了。 这是我的代码:

 package SwingWorkerExampleCopy; import java.util.List; import java.util.Random; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.SwingWorker; import javax.swing.border.EmptyBorder; import javax.swing.table.DefaultTableModel; import java.awt.BorderLayout; public class SwingWorkerExampleCopy { public static void main(String[] args) { // Show GUI java.awt.EventQueue.invokeLater(new Runnable() { @Override public void run() { GUI gui = new GUI(); DefaultTableModel tableModel = new DefaultTableModel(); // Use a SwingWorker Worker worker = new Worker(tableModel); worker.execute(); JTable table = new JTable(tableModel); table.setEnabled(false); // table.setTableHeader(null); JScrollPane scrollPane = new JScrollPane(table); gui.getContentPane() .add(scrollPane, BorderLayout.CENTER); } }); } } class GUI extends JFrame { private static final long serialVersionUID = 1L; public GUI() { setTitle("GUI"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(100, 100, 350, 400); setLocationRelativeTo(null); setVisible(true); JPanel contentPane = new JPanel(); contentPane.setBorder(new EmptyBorder(10, 10, 10, 10)); contentPane.setLayout(new BorderLayout(0, 0)); setContentPane(contentPane); } } class Worker extends SwingWorker { private final static int numRows = 10; private final static int numCols = 10; private DefaultTableModel model; Worker(DefaultTableModel model) { this.model = model; model.setColumnCount(numCols); } @Override protected DefaultTableModel doInBackground() throws Exception { // Add row for (int row = 0; row < numRows; row++) { // Build columns for (int col = 0; col < numCols; col++) { if (col == 0) { publish(new Object[] { new String("Row " + row), row, col }); } else { // Simulate a slow source Thread .sleep(new Random().nextInt((250 - 50) + 1) + 50); Boolean isSuccessful = false; // Simulate a return value if (new Random().nextBoolean()) { isSuccessful = true; } publish(new Object[] { new String((isSuccessful == true ? "x" : "o")), row, col }); if (isSuccessful == true) { break; } } } } return model; } @Override protected void process(List chunks) { for (Object[] chunk : chunks) { // chunk[0]: cell value // chunk[1]: number // chunk[2]: column if ((int) chunk[2] == 0) { Object[] row = new Object[numCols]; row[0] = (Object) chunk[0]; model.addRow(row); } else { model.setValueAt((Object) chunk[0], (int) chunk[1], (int) chunk[2]); } } } } 

因为在代码运行时,没有其他事件(例如重绘事件)可以执行 – 您将阻止事件线程,直到完成为止。

您可以直接调用repaint,但在代码运行时,UI仍然无法响应输入。 最好在单独的工作线程中运行循环,并在需要时使用invokeLater或invokeAndWait执行UI更新。