绘制组件设置为BufferedImage导致显示故障 [英] Drawing a Component to BufferedImage causes display corruption

查看:342
本文介绍了绘制组件设置为BufferedImage导致显示故障的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是用描述的 JScrollNavigator 组件的此处,以提供一个导航窗口到大的画布般的CAD我已经嵌入组件 JScrollPane的

I am using the JScrollNavigator component described here, in order to provide a navigation window onto a large "canvas-like" CAD component I have embedded within a JScrollPane.

我试图适应 JScrollNavigator 绘制在画布的缩略图提供一些附加的上下文给用户。但是,这样做的动作使我的应用程序的主框架的渲染被损坏。具体来说,就是调用油漆(图形)的视域组件(也就是我的主画布)上,传递图形按创建对象的BufferedImage 导致后续显示腐败;如果我评论这一行了,一切工作正常。

I have tried to adapt the JScrollNavigator to draw a thumbnail image of the canvas to provide some additional context to the user. However, the action of doing this causes the rendering of my application's main frame to become corrupted. Specifically, it is the action of calling paint(Graphics) on the viewport component (i.e. my main canvas), passing in the Graphics object created by the BufferedImage that causes subsequent display corruption; if I comment this line out everything works fine.

下面是的重写的paintComponent 方法 JScrollNavigator

@Override
protected void paintComponent(Graphics g) {
    Component view = jScrollPane.getViewport().getView();
    BufferedImage img = new BufferedImage(view.getWidth(), view.getHeight(), BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2d = img.createGraphics();

    // Paint JScrollPane view to off-screen image and then scale.
    // It is this action that causes the display corruption!
    view.paint(g2d);
    g2d.drawImage(img, 0, 0, null);
    Image scaled = img.getScaledInstance(getWidth(), getHeight(), 0);

    super.paintComponent(g);
    g.drawImage(scaled, 0, 0, null);
}

有没有人有任何建议,腐败的原因是什么?我本来以为画到屏幕外图像应该对现有的油漆操作没有任何影响。

Does anyone have any suggestions as to the cause of the corruption? I would have thought that painting to an offscreen image should have no effect on existing paint operations.

修改

要提供一些额外的细节: JScrollNavigator 上形成的左手侧子面板调整JSplitPane 。与导航相关的 JScrollPane的是在右侧。 腐败导致分离器不再被渲染并且滚动条不可见(它们出现白色)。如果我调整的JFrame JMenu的部分也变为白色。如果我试图使用导航或滚动条互动,他们变得​​可见,但分离器保持白色。这是因为如果各组成部分的不透明设置已受视视图渲染到屏幕外图像。

To provide some additional detail: The JScrollNavigator forms a sub-panel on the left-hand side of a JSplitPane. The JScrollPane associated with the navigator is on the right-hand side. The "corruption" causes the splitter to no longer be rendered and the scrollbars to not be visible (they appear white). If I resize the JFrame, the JMenu section also becomes white. If I attempt to use the navigator or interact with the scrollbars, they become visible, but the splitter remains white. It's as if the opaque settings of the various components has been affected by the rendering of the viewport view to an offscreen image.

另外,如果我让 JScrollNavigator 出现在一个完全独立的的JDialog ,一切正常。

Also, if I make the JScrollNavigator appear in a completely separate JDialog, everything works correctly.

编辑2

我可以重现该问题的始终通过执行以下操作:

I can reproduce the problem consistently by doing the following:

添加的JMenuBar mFrame

JMenuBar bar = new JMenuBar();
bar.add(new JMenu("File"));
mFrame.setJMenuBar(bar);

JScrollNavigator 的main()替换方法:

jsp.setViewportView(textArea);

...有:

jsp.setViewportView(new JPanel() {
  {
    setBackground(Color.GREEN);
    setBorder(BorderFactory.createLineBorder(Color.BLACK, 5));
  }
});

确保 JScrollNavigator 被嵌入在面板 mFrame ,而不是表现为一个单独的的JDialog

Ensure that the JScrollNavigator is embedded as a panel within mFrame, rather than appearing as a separate JDialog:

mFrame.add(jsp, BorderLayout.CENTER);
mFrame.add(nav, BorderLayout.NORTH);

该应用程序运行时

现在的的JMenuBar 不再可见;绘画的观点(即绿色的JP​​anel 厚厚的黑色边框),以行为的的Graphics2D 按<$ C返回$ C> BufferedImage.createGraphics()实际上似乎使得它的屏幕,可能从JFrame的左上角,从而掩盖其他成分。这似乎只发生,如果一个的JP​​anel 用作口的视图,而不是其他组件,如的JTextArea 的JTable

Now when the application runs the JMenuBar is no longer visible; the act of painting the view (i.e. a green JPanel with thick black border) to the Graphics2D returned by BufferedImage.createGraphics() actually appears to be rendering it onscreen, possibly from the top-left corner of the JFrame, thus obscuring other components. This only seems to happen if a JPanel is used as the viewport view, and not another component such as JTextArea, JTable, etc.

编辑3

看起来像这个人有同样的问题(无解贴虽然):的http:/ /www.javaworld.com/community/node/2894/

Looks like this person was having the same problem (no solution posted though): http://www.javaworld.com/community/node/2894/

修改4

这里的的paintComponent 这导致编辑2所述的可重复的错误方法:

Here's the main and paintComponent methods that result in the reproducible error described in Edit 2:

public static void main(String[] args) {
    JScrollPane jsp = new JScrollPane();
    jsp.setViewportView(new JPanel() {
        {
            setBackground(Color.GREEN);
            setBorder(BorderFactory.createLineBorder(Color.BLACK, 5));
        }
    });

    JScrollNavigator nav = new JScrollNavigator();
    nav.setJScrollPane(jsp);

    JFrame mFrame = new JFrame();

    JMenuBar bar = new JMenuBar();
    bar.add(new JMenu("File"));
    mFrame.setJMenuBar(bar);

    mFrame.setTitle("JScrollNavigator Test");

    mFrame.setSize(800, 600);

    mFrame.setLayout(new GridLayout(1, 2));

    mFrame.add(jsp);
    mFrame.add(nav);
    Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
    mFrame.setLocation((screenDim.width - mFrame.getSize().width) / 2, (screenDim.height - mFrame.getSize().height) / 2);

    mFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    mFrame.setVisible(true);
}

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);

    Component view = jScrollPane.getViewport().getView();

    if (img == null) {
        GraphicsConfiguration gfConf = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
        img = new BufferedImage(view.getWidth(), view.getHeight(), BufferedImage.TYPE_INT_ARGB);
    }

    Graphics2D g2d = img.createGraphics();
    view.paint(g2d);

    Image scaled = img.getScaledInstance(getWidth(), getHeight(), 0);

    g.drawImage(scaled, 0, 0, null);
}

修改5

好像别人都很难再现确切的问题。我想请人跑code粘贴 rel=\"nofollow\">。当我第一次运行这个例子我看到以下内容:

It seems like others are having trouble recreating the exact problem. I would ask people to run the code pasted here. When I first run this example I see the following:

无论是JScrollNavigator或JMenuBar的都被涂;这些框架的区域是透明的。

Neither the JScrollNavigator or the JMenuBar have been painted; these frame areas are transparent.

调整我看到以下后:

的JMenuBar 仍然没有被涂,看来,在的JP​​anel 是在在呈现一些点(0 ,0)(其中的JMenuBar 应该是)。 的paintComponent view.paint 调用是这样做的直接原因。

The JMenuBar has still not been painted and it appears that the JPanel was at some point rendered at (0,0) (where the JMenuBar should be). The view.paint call within paintComponent is the direct cause of this.

推荐答案

摘要:原 JScrollNavigator 使用了Swing的 透明度 属性呈现绿色方便 NavBox 通过缩放缩略图在相邻组件的 JScrollPane的。因为它扩展的JP​​anel ,(共享)UI委托的使用不透明与滚动元件的冲突。在以上5编辑看到的图像典型化相关的渲染神器,也显示这里。解决的办法是让 NavBox JScrollNavigator 和滚动元件延长的JComponent ,如在下面的第二增编建议。每个组件然后可以管理它单独自己的属性。

Summary: The original JScrollNavigator uses the Swing opacity property to render a convenient green NavBox over a scaled thumbnail of the component in an adjacent JScrollPane. Because it extends JPanel, the (shared) UI delegate's use of opacity conflicts with that of the scrollable component. The images seen in edit 5 above typify the associated rendering artifact, also shown here. The solution is to let NavBox, JScrollNavigator and the scrollable component extend JComponent, as suggested in the second addendum below. Each component can then manage it's own properties individually.

我看到你的 code作为贴我的平台的Mac OS X上没有任何不寻常的渲染神器, Java 1.6的。对不起,我没有看到任何明显的便携性侵犯。

I see no unusual rendering artifact with your code as posted on my platform, Mac OS X, Java 1.6. Sorry, I don't see any glaring portability violations.

一些可能无关紧要,但也许有用,观察。

A few probably irrelevant, but perhaps useful, observations.


  • 即使你使用的setSize(),适当地在这种情况下,你还是应该包()封闭窗口

  • Even if you use setSize(), appropriately in this case, you should still pack() the enclosing Window.

f.pack();
f.setSize(300, 200);


  • 为了方便起见,添加()转发组件内容窗格。

    f.add(nav, BorderLayout.WEST);
    


  • preFER 的StringBuilder 的StringBuffer

    考虑 ComponentAdapter 代替的ComponentListener

    附录:由于这里建议,我稍微用更灵活的结果的RenderingHints ,而不是 getScaledInstance(),如下图所示。添加几个图标可以更容易地看到图像和文本的不同效果。

    Addendum: As suggested here, I got somewhat more flexible results using RenderingHints instead of getScaledInstance() as shown below. Adding a few icons makes it easier to see the disparate effect on images and text.

    editPane.insertIcon(UIManager.getIcon("OptionPane.errorIcon"));
    editPane.insertIcon(UIManager.getIcon("OptionPane.warningIcon"));
    ...
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Component view = jScrollPane.getViewport().getView();
        BufferedImage img = new BufferedImage(view.getWidth(),
            view.getHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics2D off = img.createGraphics();
        off.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        off.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
            RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        view.paint(off);
        Graphics2D on = (Graphics2D)g;
        on.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        on.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
            RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        on.drawImage(img, 0, 0, getWidth(), getHeight(), null);
    }
    

    附录孔型:它看起来像的JP​​anel UI委托不合作。一个解决办法是延长的JComponent ,这样就可以控制的透明度。这只是稍微更多的工作来管理的backgroundColor NavBox JScrollNavigator 也是类似的治疗的候选者。

    Addendum secundum: It looks like the JPanel UI delegate is not cooperating. One workaround is to extend JComponent so that you can control opacity. It's only slightly more work to manage the backgroundColor. NavBox and JScrollNavigator are also candidates for a similar treatment.

    jsp.setViewportView(new JComponent() {
    
        {
            setBackground(Color.red);
            setBorder(BorderFactory.createLineBorder(Color.BLACK, 16));
        }
    
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setColor(getBackground());
            g.fillRect(0, 0, getWidth(), getHeight());
        }
    
        @Override
        public Dimension getPreferredSize() {
            return new Dimension(300, 300);
        }
    });
    

    这篇关于绘制组件设置为BufferedImage导致显示故障的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆