【问题标题】:Custom JComponent with embedded Swing components not positioned in exported image带有嵌入式 Swing 组件的自定义 JComponent 未定位在导出的图像中
【发布时间】:2011-04-04 12:36:37
【问题描述】:

我在尝试将自定义 Java JPanel 导出到 PNG 文件时遇到了一个有趣的问题。到目前为止,我一直在编写的组件的导出过程完美无缺。

我的 JPanel 包含自定义编写的 JComponent(例如,覆盖 paintComponent(Graphics g) 并编写我必须写的内容)。

导出过程如下所示(我拥有的扩展 JPanel):

 public void export(File file, int width, int height)
  throws IOException
{
     Dimension size = getSize();

     BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
     Graphics2D g2 = image.createGraphics();
     draw (g2, new Rectangle (0, 0, width, height));

     try {
         ImageIO.write(image, "png", file);
     } catch (FileNotFoundException e) {
         throw new IOException ("Unable to export chart to ("
               + file.getAbsolutePath() + "): " + e.getLocalizedMessage());
     } finally {
         g2.dispose();
     }
}

上面的 'draw()' 方法会导致 JPanel 的所有子组件使用要导出的图像的新大小重新绘制。效果很好。

我今天遇到的问题是我有一个自定义 JPanel,其中包含一些 Swing 组件(一个 JScrollPane 包装了一个 JEditorPane)。这个 JPanel 包括我的一个自定义 JComponent,然后是第二个 JComponent,上面有 JScrollPane。

大约 75% 的时间,当我执行导出时,带有 JScrollPane 的第二个 JComponent 没有正确定位在导出的图像中。它位于 Point (0, 0) 处,大小就是它在屏幕上的样子。此 JComponent 的“draw()”方法如下所示:

public void draw(Graphics2D g2, Rectangle componentArea) {

    scrollPane.setBounds(componentArea);
    textArea.setText(null);
    sb.append("<html>");
    sb.append("<h1 style=\"text-align:center;\">" + "XXXXXXXXX  XXXXXXX" + "</h1>");
    textArea.setText(sb.toString());

    super.paintComponents(g2);
}

但大约有 25% 的情况下这是可行的 - 这个带有滚动窗格的 JComponent 正确定位在我导出的图像中。重新绘制组件工作。

好像这里发生了一些我无法弄清楚的双缓冲....

想法?

【问题讨论】:

    标签: java swing jcomponent paintcomponent


    【解决方案1】:

    您是否通过任何更改修改自定义组件中提供的 Graphics 对象的 Transform 对象?如果您确实确保首先保存它,然后为您的目的修改一个新实例,当您完成后,将旧的转换设置回来。

    【讨论】:

    • 不,我不知道。自定义 JComponents 刚刚在新图像的图形上下文中绘制了自己。他们使用传入的 Rectangle 自己做了(做)所有位的低级定位。
    【解决方案2】:

    我会尝试调用paint() 方法,而不是paintComponents()。

    可能是因为您正在设置编辑器窗格的文本,当您尝试绘制组件时,文本没有被正确解析并且文档未处于最终状态。或者可能是因为您正在动态设置您遇到问题的组件的边界。尝试将 super.paint() 方法包装在 SwingUtilities.invokeLater() 中。

    ScreenImage 类是我用来创建图像的。但我一直用它来创建静态 GUI 的图像。那就是我不会在尝试创建图像的同时更改组件的边界。

    【讨论】:

      【解决方案3】:

      找到了解决办法!!你关于“安排绘画活动”的问题在我脑海中萦绕了一会儿。几年前我遇到过这样的问题,然后忘记了。年纪大了会这样....

      解决方案是将“draw()”方法包装在“SwingUtilities.invokeAndWait()”中。瞧!我的 'export()' 方法现在看起来像:

          public void export(File file, final int width, final int height)
          throws IOException
      {
      
          BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
          final Graphics2D g2 = image.createGraphics();
      
          //  Must wait for the bloody image to be drawn as Swing 'paint()' methods
          //  merely schedule painting events.  The 'draw()' below may not complete
          //  the painting process before the 'write()' of the image is performed.
      
          //  thus, we wait....
      
          try {
              SwingUtilities.invokeAndWait(new Runnable() {
                  public void run() {
                      draw (g2, new Rectangle (0, 0, width, height));
                  }
              });
              ImageIO.write(image, "png", file);
          } catch (FileNotFoundException e) {
              throw new IOException ("Unable to export chart to ("
                      + file.getAbsolutePath() + "): " + e.getLocalizedMessage());
          } catch (InterruptedException e) {
              e.printStackTrace();
              throw new IOException ("Unable to export chart to ("
                      + file.getAbsolutePath() + "): " + e.getLocalizedMessage());
          } catch (InvocationTargetException e) {
              e.printStackTrace();
              throw new IOException ("Unable to export chart to ("
                      + file.getAbsolutePath() + "): " + e.getLocalizedMessage());
          } finally {
              g2.dispose();
          }
      }
      

      哇!

      【讨论】:

      • 如果在 EDT 上调用,这将挂起 GUI!这让我认为问题在于导出代码实际上是在非 EDT 线程上调用绘制。可能,在 Swing 内部的某个地方,某个方法确定它不在 EDT 上并进行了 invokeLater 调用,从而推迟了实际的绘制。
      【解决方案4】:

      Swing 组件的布局通常是延迟发生而不是立即发生,因此可能会导致您的行为出现间歇性。您可以尝试直接调用 scrollPane.doLayout() - 通常这是一个坏主意,但它应该保证在您绘制它之前布局 scrollPane。

      此外,如果要绘制到屏幕外图像,您可能应该调用 printAll(g) 而不是 paintComponents(g),因为这样可以避免双缓冲问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-06-06
        • 1970-01-01
        • 2011-11-16
        相关资源
        最近更新 更多