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

发布于 2024-09-16 08:01:59 字数 1618 浏览 3 评论 0原文

我在尝试将自定义 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 未正确定位在导出的图像中。它位于点 (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 正确定位在我导出的图像中。重新绘制组件即可。

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

想法?

I have a funny problem trying to export custom Java JPanels to a PNG file. The export process of the components I've been writing up until now have worked flawlessly.

My JPanels include custom-written JComponents (e.g., override paintComponent(Graphics g) and write what I have to).

The export process looks like the following (of the extended JPanel I have):

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

The 'draw()' method above causes all of the JPanel's child components to be re-drawn using the new size of the image to be exported. Works very well.

The problem I have today is that I have one custom JPanel that includes some Swing components (a JScrollPane wrapping a JEditorPane). This JPanel includes one of my custom JComponents and then this second JComponent with the JScrollPane on it.

About 75% of the time, this second JComponent with the JScrollPane is not positioned correctly in the exported image when I perform the export. It is positioned at Point (0, 0) and the size is what it looks like on the screen. The 'draw()' method for this JComponent looks like the following:

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

But about 25% of the time this works - this JComponent with the scrollpane is correctly positioned in my exported image. The re-draw the componment works.

It is like there is some double-buffering going on here that I can't figger out....

Ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

春花秋月 2024-09-23 08:01:59

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

Do you by any change modify the Transform object of the provided Graphics object in your custom Component? If you do make sure to save it first and then modify a new instance for your purposes and when you are done, set the old Transform back.

我的痛♀有谁懂 2024-09-23 08:01:59

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

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

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

I would try invoking the paint() method, not paintComponents().

Maybe because you are setting the text of the editor pane the text hasn't been properly parsed and the Document isn't in a final state when you attempt to paint the component. Or maybe because you are dynamically setting the bounds of components you have problems. Try wrapping the super.paint() method in a SwingUtilities.invokeLater().

The ScreenImage class is what I use to create images. But I've always used it to create images of static GUI's. That is I don't alter the bounds of components at the same time a try to create an image.

水波映月 2024-09-23 08:01:59

找到解决办法了!!你关于“安排绘画活动”的评论在我脑海中盘旋了一段时间。几年前我遇到过这样的问题,后来忘记了。老年会这样做......

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

哇!

Found a solution!! Your comments about 'scheduling painting events' rang around in my head for a bit. I had a problem like this some years ago and forgot about that. Old age will do that....

The solution is to wrap the 'draw()' method in a 'SwingUtilities.invokeAndWait()'. Voila! My 'export()' method now looks like:

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

Whew!

じ违心 2024-09-23 08:01:59

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

另外,为了绘制离屏图像,您可能应该调用 printAll(g) 而不是 PaintComponents(g),因为这样可以避免双缓冲问题。

Layout of Swing components usually occurs lazily rather than immediately so that might be causing your intermittent behaviour. You could try calling scrollPane.doLayout() directly - usually this is a bad idea but it should guarantee that scrollPane is laid out before you paint it.

Also for painting to an off-screen image you should probably call printAll(g) rather than paintComponents(g) as that avoids double buffering issues.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文