【问题标题】:Dynamically adding rows to JTable - Why do they appear at once?将行动态添加到 JTable - 为什么它们会立即出现?
【发布时间】:2014-03-25 09:04:18
【问题描述】:

在示例中,我试图向我的 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);
  }
}

【问题讨论】:

  • 不要让 EDT 睡觉 - 永远
  • EDT 是 Event Dispatch Thread 的缩写。现在应该直观地明白为什么会发生这种情况。 :)
  • 非常感谢您的提示;我更新了这个例子。我是不是又让事情变得更糟了?! :-)
  • 谢谢,我现在就这么做了!

标签: java swing jtable swingworker thread-sleep


【解决方案1】:

重申克利奥帕特拉:不要让 EDT 睡觉

您可以改用javax.swing.Timer,如this answer中所示


编辑

我不想过多地弄乱您的代码(只是因为它看起来对我来说很奇怪),但我对其进行了一些更改以添加计时器

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++;
    }
}

【讨论】:

  • 我更新了我的例子;看起来我仍然做错了什么(即使在删除了 Thread.sleep() 问题之后)。我不明白!
  • 您仍在 EDT 上运行一个长时间运行的进程,这将阻止它,并且不会给您想要的效果。为什么没有按照我的建议实现 Timer?
  • 我这样做是为了测试,但它并没有做我想做的事情。我真的不知道任务需要多长时间,所以这真的不仅仅是化妆品。
  • 查看我的编辑。不确定您要做什么,但我添加了计时器来向您展示。
  • 查看我的更新,我使用 SwingWorker 解决了任务。
【解决方案2】:

正如 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<DefaultTableModel, Object[]> {

  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<Object[]> 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]);
      }
    }
  }
}

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • 阻塞事件线程by Thread.sleep(250);直到你完成。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-22
    • 1970-01-01
    • 2017-01-08
    相关资源
    最近更新 更多