绘制组件设置为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
绘制在画布的缩略图提供一些附加的上下文给用户。但是,这样做的动作使我的应用程序的主框架的渲染被损坏。具体来说,就是调用油漆(图形)
的视域组件(也就是我的主画布)上,传递图形$ C的作用$ C>按创建对象
的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
不再可见;绘画的观点(即绿色的JPanel
厚厚的黑色边框),以行为的的Graphics2D
按 BufferedImage.createGraphics()实际上似乎使得它的屏幕,可能从JFrame的左上角,从而掩盖其他成分。这似乎只发生,如果一个的JPanel
用作口的视图,而不是其他组件,如的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
仍然没有被涂,看来,在的JPanel
是在在呈现一些点(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的
。因为它扩展的JPanel
,(共享)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 stillpack()
the enclosingWindow
.
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);
}
附录孔型:它看起来像的JPanel
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);
}
});