2010-08-26 58 views
0

尝试将自定义Java JPanel导出到PNG文件时出现了一个有趣的问题。到目前为止,我一直在撰写的组件的出口过程完美无瑕。嵌入式Swing组件未定位在导出图像中的自定义JComponent

我的JPanels包含自定义编写的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(); 
    } 
} 

的'画()方法将导致所有的JPanel的子组件的使用新的重新绘制要导出图像的大小。工作得很好。

我今天遇到的问题是我有一个自定义JPanel,它包含一些Swing组件(一个包装JEditorPane的JScrollPane)。这个JPanel包含我的一个自定义JComponents,然后是第二个带有JScrollPane的JComponent。

大约75%的时间,当我执行导出时,带有JScrollPane的第二个JComponent未在导出的图像中正确定位。它位于Point(0,0)处,大小与它在屏幕上的样子相同。对于此JComponent的'画()方法如下所示:

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% - 此模型上具有ScrollPane正确定位在我的导出图像。重新绘制组件作品。

好像是有一些双缓冲会在这里,我不能figger出来....

想法?

回答

0

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

+0

不,我不知道。自定义的JComponents刚刚绘制在新图像的图形上下文中。他们使用传入的矩形做了(做)所有的低位定位。 – redBeard 2010-08-26 21:37:56

0

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

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

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

+0

嗯,没有用。相同的结果..... – redBeard 2010-08-27 16:26:50

0

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

同样为了画到屏幕外的图像,你可能应该调用printAll(g)而不是paintComponents(g),因为这样可以避免双重缓冲问题。

+0

嗯,没有变化。相同的结果.... – redBeard 2010-08-27 16:28:29

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

噢!

+0

如果在EDT上调用,将挂起GUI!这让我觉得这个问题实际上是导出代码在非EDT线程上调用paint。可能在Swing的深处某处,有些方法确定它不在EDT上,并调用invokeLater,从而推迟实际绘画。 – 2010-08-31 16:39:19