Java 带有嵌入Swing组件的自定义JComponent未定位在导出的映像中

Java 带有嵌入Swing组件的自定义JComponent未定位在导出的映像中,java,swing,jcomponent,paintcomponent,Java,Swing,Jcomponent,Paintcomponent,我在尝试将自定义Java JPanel导出到PNG文件时遇到了一个有趣的问题。到目前为止,我一直在写的组件的导出过程工作得完美无缺 我的JPanel包括自定义编写的JComponents,例如,重写paintComponentGraphics g并编写我必须编写的内容 导出过程与我拥有的扩展JPanel类似: public void export(File file, int width, int height) throws IOException { Dimension si

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

我的JPanel包括自定义编写的JComponents,例如,重写paintComponentGraphics 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组件—一个包装JEditorPane的JScrollPane。这个JPanel包括我的一个定制JComponent,然后是第二个JComponent,上面有JScrollPane

大约75%的情况下,当我执行导出时,带有JScrollPane的第二个JComponent在导出的图像中没有正确定位。它位于点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在我导出的图像中的位置是正确的。重新绘制组件工作

这就像是有一些双缓冲在这里进行,我不能figger出来


想法?

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

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

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


这个类是我用来创建图像的。但我总是用它来创建静态GUI的图像。也就是说,我不会在尝试创建映像的同时更改组件的边界。

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


另外,对于非屏幕图像的绘制,您可能应该调用printAllg而不是paintComponentsg,因为这样可以避免双重缓冲问题。

找到了解决方案!!你关于“安排绘画活动”的评论在我脑海里回荡了一会儿。几年前我遇到过这样的问题,但我忘记了。年纪大了就可以了

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

    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();
    }
}

不,我没有。自定义JComponents刚刚在新图像的图形上下文上绘制了它们自己。他们确实使用传入的矩形自己完成了位的所有低级定位。如果在EDT上调用,这将挂起GUI!这让我觉得问题在于导出代码在非EDT线程上调用paint。也许,在Swing内部深处的某个地方,某些方法确定它不在EDT上,并发出调用器调用,从而推迟了实际绘制。