【问题标题】:Painting JComponent as a long task将 JComponent 绘制为一项长期任务
【发布时间】:2017-05-15 14:57:22
【问题描述】:
  1. 与 Swing JComponent 的所有交互都必须在事件调度线程中完成。也会画画。
  2. 长任务必须在 Event Dispatch Thread 之外运行,否则会阻塞 GUI。

但是如果长任务是在 Graphics2D 中绘制应用程序,例如 1000 次呢?这是相互矛盾的要求吗?

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

如果我需要多次调用这个方法,这可以被认为是一个漫长的任务,我应该怎么做,以避免阻塞GUI?据我了解,仅将其委托给 SwingWorker 是不允许的。这种“长任务”有什么解决方法吗?

【问题讨论】:

  • 你能解释一下为什么要花这么多时间吗?它是否访问数据库?它会调用一个长时间运行的事务吗?另一个问题:为什么它会运行 1000 次?那 1000 次油漆你有多少时间?
  • 在paintComponent 方法之外计算所有的数字和形状,所以paintComponent 方法需要做的就是,好吧,绘制。仅绘制一千个对象不太可能影响性能,尤其是在配备现代显卡的机器上。
  • @Tamas Rev 是的,实际上我绘制了我的应用程序 10.000 多次以生成 pdf(只需使用 pdf-g2 调用paintComponent)。我想在执行此操作时显示进度条。
  • painting my appllication 10.000+ times to generate pdfs - 对我来说毫无意义。为什么要重绘 10K 次才能生成 PDF。你是做动画的吗?用户永远不会注意到 PDF 中的 10K 增量更改。如果您正在生成 10K PDF,那么 Swing 与此有什么关系?我猜PDF是在后台生成并保存的。如果您只想更新进度条,则无需自定义绘画。阅读How to Use Progress Bars 上的教程以获取工作示例。
  • @camickr 实际上我正在为庞大的用户群制作个性化的空白。请不要使用“稻草人”这么咄咄逼人和明显。绘制应用程序 10k+ 次可能听起来很奇怪,但仍然是一项可能的任务。

标签: java multithreading swing paintcomponent swingworker


【解决方案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(),因此不会冻结您的用户界面。这就是它不需要任何执行程序或工作线程的原因。它只是确保它添加了

【讨论】:

  • 第一个 paintComponent 永远不应该是 public,它是 protected 出于某种原因,永远不需要直接调用它,而是应该使用 print 或 @987654328 @ 是为这种操作设计的,为一个禁用双缓冲
猜你喜欢
  • 2013-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-27
  • 1970-01-01
  • 2018-02-10
  • 2012-01-21
  • 1970-01-01
相关资源
最近更新 更多