将JComponent绘制为一项长任务

时间:2017-05-15 14:57:23

标签: java multithreading swing paintcomponent swingworker

  1. 与Swing JComponent的所有交互必须在Event Dispatch Thread中完成。画也。
  2. 长任务必须在Event Dispatch Thread之外运行,否则它们将阻止GUI。
  3. 但是如果长期任务是在Graphics2D中绘制应用程序1000例怎么办?这是相互矛盾的要求吗?

    public class MyObject extends JPanel {
        ...
        public void paintComponent(Graphics g) {...}
        ...
    }
    

    如果我需要多次调用此方法,它可以被视为一项长期任务,我应该怎么做,以避免阻止GUI?据我所知,不允许在SwingWorker中委托它。对于这种“长期任务”有没有解决办法?

1 个答案:

答案 0 :(得分:1)

我认为这是一个例外。只要您没有绘制到屏幕上,就可以在后台线程中调用它。关键是实际的UI内容应该发生在事件派发线程上,这样所有的更改都将对用户可见。

这是如何发生这种情况的一个框架:

public class MyObject extends JPanel {

    private static final int NUMBER_OF_PDFS = 10_000;

    private JProgressBar progressBar = new JProgressBar(0, NUMBER_OF_PDFS);

    public void paintPdfs() {
        ExecutorService threadPool = Executors.newFixedThreadPool(5); // this can come somewhere else too
        for (int i = 0; i < NUMBER_OF_PDFS; i++) {
            final int newProgressBarValue = i; // you might need some mapping, depends on the setup of the taskbar
            threadPool.execute(() -> {
                try {
                    Graphics pdfG2 = getPdfGraphics();
                    MyObject.this.paintComponent(pdfG2);
                } finally {
                    SwingUtilities.invokeLater(() -> {
                        int progressBarValue = progressBar.getValue();
                        if (progressBarValue < newProgressBarValue) {
                            progressBar.setValue(newProgressBarValue);
                        }
                    });
                }
            });
        }
    }

    private Graphics getPdfGraphics() {
        // I don't know how to do this. On the other hand, you do :)
        return null;
    }

    @Override
    public void paintComponent(Graphics g) {
        // ...
    }
}

我只能看到一个警告:在此打印过程中,摆动物体不应改变。如果他们这样做,那么你需要另一个技巧。另一个技巧是逐个在事件派发线程中打印pdf:

public void paintPdfs() {
    for (int i = 0; i < NUMBER_OF_PDFS; i++) {
        final int newProgressBarValue = i; // you might need some mapping, depends on the setup of the taskbar
        SwingUtilities.invokeLater(() -> {
            try {
                Graphics pdfG2 = getPdfGraphics();
                MyObject.this.paintComponent(pdfG2);
            } finally {
                int progressBarValue = progressBar.getValue();
                if (progressBarValue < newProgressBarValue) {
                    progressBar.setValue(newProgressBarValue);
                }
            }
        });
    }
}

第二种方法一次运行一个paintComponent(),因此不会冻结您的UI。这就是为什么它不需要任何执行程序或工作线程。它只是确保它添加