具有背景图像和剪切矩形的 JTextPane 问题
我在使用 Swing 时遇到问题,但找不到原因。我有一个 JTextPane 已扩展为显示背景图像。这可以是光栅图像(通过标准 Java API 显示)或 SVG 矢量图像(通过 SVG Salamander 显示)。
由于我希望文本窗格的顶部有一个区域用作边距,不会显示任何文本,因此我执行以下操作:我重写 PaintComponent(),绘制背景图像,然后调用 super.paintComponent() 以便将显示文本等,最后我再次绘制一块背景图像,但使用剪切矩形仅覆盖顶部边距区域中的文本。
除了我几天来一直在努力解决的一个小问题之外,这工作得很好:对于光栅图像,如果我在文本窗格中选择文本,文本将被删除而不是突出显示。也就是说,当我选择文本时,背景图像会显示在我选择的部分上。我不明白为什么会这样,因为绘制图像的第一个调用是在 super.paintComponent() 之前调用的,第二个调用有一个剪切矩形,因此它只在边缘上绘制,如果我不这样做,一切都会正常工作不要做出选择。一些额外的线索:
- 我确实知道这与第二次通话有关,因为如果我评论它,我就没有这个问题(但我没有余量)。
- 奇怪的是,当背景是矢量图像而只有光栅图像时,这种情况不会发生。
这是我的paintComponent()方法的代码:
public void paintComponent(Graphics g)
{
Rectangle rect = null;
if ( rasterBackgroundImage != null )
{
rect = getVisibleRect();
g.drawImage(rasterBackgroundImage.getImage(),rect.x,rect.y,rect.width,rect.height,this);
}
if ( vectorBackgroundImage != null )
{
rect = getVisibleRect();
vectorBackgroundImage.setPreferredSize(new Dimension(rect.width,rect.height));
vectorBackgroundImage.setScaleToFit(true);
vectorBackgroundImage.paintIcon(this, g, rect.x, rect.y);
}
super.paintComponent(g);
//if we want a non-scrolling top margin
if ( rasterBackgroundImage != null )
{
g.setClip(rect.x,rect.y,rect.width,getMargin().top);
g.drawImage(rasterBackgroundImage.getImage(),rect.x,rect.y,rect.width,rect.height,this);
}
if ( vectorBackgroundImage != null )
{
g.setClip(rect.x,rect.y,rect.width,getMargin().top);
vectorBackgroundImage.setPreferredSize(new Dimension(rect.width,rect.height));
vectorBackgroundImage.paintIcon(this, g, rect.x, rect.y);
}
}
如果有人想查看整个类,就在这里:http://code.google.com/p/aetheria/source/browse/trunk/age/src/eu/irreality/age/swing/FancyJTextPane.java?r=301
请注意,我'我不要求修复,因为问题似乎出在与其他类的交互中。这就是为什么我没有提供 SSCCE:我尝试构建一个,但如果我单独使用这个类......它实际上是有效的。我无法在整个系统之外重现该问题,并且我不知道是哪种交互产生的。但我将非常感谢任何人提供提示,为我指明正确的方向 - 也许有人以前见过这种事情,并且可以知道可能的原因......
更新:我已经通过停止使用 setClip() 设法解决了这个问题。我发现这个答案建议不要在paintComponent()中使用setClip():java swing裁剪问题
我现在不使用剪切矩形,而是创建一个子图像,其中包含要在边缘绘制的图像的顶部部分,并直接绘制它而不调用 setClip()。这可能效率很低,因为我在内存中存储了两张图像,而一张图像就足够了,但至少它可以工作(tm)。如果有人对这个黑客感到好奇,就在这里(目前代码有点脏):http://code.google.com/p/aetheria/source/browse/trunk/age/src/eu/irreality/age/swing/FancyJTextPane.java?r=305
仍然,如果有人能够找出以这种方式使用 setClip() 导致这些问题的确切原因,或者知道解决这个问题的有效方法,这会很有趣。感谢所有的答案! :)
I have a problem with Swing that I just don't find the reason for. I have a JTextPane that has been extended to show a background image. This can be either a raster image (shown via the Standard Java APIs) or a SVG vector image (shown via SVG Salamander).
Since I want the text pane to have an area at the top used as a margin that won't show any text, I do the following: I override paintComponent(), paint the background image, then call super.paintComponent() so that the text and so on will be shown, and finally I paint a piece of the background image again but with a clipping rectangle to cover only the text that is in the top margin area.
This works perfectly fine except for a little glitch that I've been battling for days: with the raster image, if I select text in the text pane, the text is removed rather than highlighted. That is, when I select text, the background image is shown over the parts that I'm selecting. I don't understand why this could be, since the first call that paints the image is called before super.paintComponent(), the second call has a clipping rectangle so it only paints over the margin, and everything works fine if I don't make selections. Some extra clues:
- I do know it's something related to the second call, since if I comment it I don't have this issue (then I don't have the margin though).
- Curiously it doesn't happen when the background is a vector image, only with a raster image.
Here is the code for my paintComponent() method:
public void paintComponent(Graphics g)
{
Rectangle rect = null;
if ( rasterBackgroundImage != null )
{
rect = getVisibleRect();
g.drawImage(rasterBackgroundImage.getImage(),rect.x,rect.y,rect.width,rect.height,this);
}
if ( vectorBackgroundImage != null )
{
rect = getVisibleRect();
vectorBackgroundImage.setPreferredSize(new Dimension(rect.width,rect.height));
vectorBackgroundImage.setScaleToFit(true);
vectorBackgroundImage.paintIcon(this, g, rect.x, rect.y);
}
super.paintComponent(g);
//if we want a non-scrolling top margin
if ( rasterBackgroundImage != null )
{
g.setClip(rect.x,rect.y,rect.width,getMargin().top);
g.drawImage(rasterBackgroundImage.getImage(),rect.x,rect.y,rect.width,rect.height,this);
}
if ( vectorBackgroundImage != null )
{
g.setClip(rect.x,rect.y,rect.width,getMargin().top);
vectorBackgroundImage.setPreferredSize(new Dimension(rect.width,rect.height));
vectorBackgroundImage.paintIcon(this, g, rect.x, rect.y);
}
}
If anyone would like to have a look at the whole class, it is here: http://code.google.com/p/aetheria/source/browse/trunk/age/src/eu/irreality/age/swing/FancyJTextPane.java?r=301
Note that I'm not asking for a fix since it seems that the problem is in interaction with other classes. That's why I didn't provide an SSCCE: I tried to build one, but if I use this class in isolation... it actually WORKS. I haven't been able to reproduce the problem outside the whole system and I have no idea which interaction produces it. But I would be very grateful to anyone providing hints pointing me in the right direction - maybe someone has seen this kind of thing before and could have a clue of what might be the cause...
Update: I have managed to work around the issue, by ceasing to use setClip(). I found this answer recommending not to use setClip() in paintComponent(): java swing clipping problem
Instead of using a clipping rectangle, I now create a subimage containing the top part of the image which I want to draw on the margin, and draw that directly without calling setClip(). It's probably quite inefficient since I'm storing two images in memory when one should be enough, but at least it Works(tm). If anyone is curious about seeing this hack, it's here (the code is a bit dirty at the moment): http://code.google.com/p/aetheria/source/browse/trunk/age/src/eu/irreality/age/swing/FancyJTextPane.java?r=305
Still if anyone is able to figure out the exact cause why using setClip() in this way causes these issues, or knows of an efficient way to solve this, it would be interesting. Thanks for all the answers! :)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果存在JLabel<,为什么要费心使用paintComponent(s)来绘制图像/a> 和如何使用图标,其他有价值的信息是描述于执行自定义绘画并在 2D 图形,大量示例 此处 和 此处 java2s.com/Code/Java/2D-Graphics-GUI/Catalog2D-Graphics-GUI.htm" rel="nofollow">2D-Graphics-GUI
why bother with painting Image by using paintComponent(s) if is there exist JLabel and How To Use Icon, other valuable informations are described in Performing Custom Painting and extended in 2D Graphics, tons examples here and 2D-Graphics-GUI
除了 @mKorbel 的有用链接之外,这里还有一些想法:
检查父容器的布局,注意插入和默认值,例如
JFrame
和BorderLayout
JPanel
的 >FlowLayout。正如您所观察到的,对比色会有所帮助。在父
窗口
上,pack()
应至少调用一次,因为它“导致此Window
的大小适合首选大小及其子组件的布局。”严格检查在一种情况下使用
setPreferredSize()
的情况,而不是在另一种情况下,注意您可能需要revalidate()
以及repaint( )
。查看如何使用编辑器窗格和文本中的示例窗格。
顺便说一句,考虑德摩根定律是否可以简化<代码>set*BackgroundImage()方法:
In addition to @mKorbel's helpful links, here are a few ideas:
Check the layout of parent containers, noting insets and defaults such as
BorderLayout
forJFrame
andFlowLayout
forJPanel
. As you've observed, contrasting colors can help.On the parent
Window
,pack()
should be called at least once, as it "causes thisWindow
to be sized to fit the preferred size and layouts of its subcomponents."Critically examine the use of
setPreferredSize()
in one case and not the other, noting that you may need torevalidate()
as well asrepaint()
.Review the examples in How to Use Editor Panes and Text Panes.
As an aside, consider whether De Morgan's laws may simplify the predicate in the
set*BackgroundImage()
methods: