具有嵌入式Swing组件的自定义JComponent未定位在导出的图像中

时间:2010-08-26 21:24:19

标签: java swing jcomponent paintcomponent

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

我的JPanel包括自定义编写的JComponents(例如,覆盖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组件(包装JEditorPane的JScrollPane)。这个JPanel包含我的一个自定义JComponents,然后是第二个带有JScrollPane的JComponent。

当我执行导出时,大约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正确定位在我导出的图像中。重新绘制成分。

就像有一些双缓冲在这里,我无法搞清楚......

想法?

4 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:0)

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

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

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

答案 2 :(得分:0)

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

另外,对于绘制到屏幕外的图像,您应该调用printAll(g)而不是paintComponents(g),因为这样可以避免双重缓冲问题。

答案 3 :(得分:0)

找到解决方案!!你对“安排绘画活动”的评论在我脑海里响了一下。几年前我遇到过这样的问题并忘了这件事。老年人会这样做....

解决方案是将'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();
    }
}

呼!