如何调试 Swing 中发生的不必要的重绘
我在正在开发的 Swing 应用程序中进行重新绘制时遇到了一些奇怪的问题。我发现当我设置某个组件的文本(它只是窗口最底部的状态栏文本标签)时,屏幕上非常不同的区域中的整个表格被告知要重新绘制。它会导致一些性能问题,因为每次我设置状态栏文本时,似乎基本上整个窗口都被要求重新绘制。
我一直在 RepaintManager 中徘徊,试图弄清楚发生了什么,并且发现当我尝试设置此文本的标签时,我的整个主 JPanel 被标记为脏组件。但是,我在调试什么时候发生这种情况以及到底为什么发生这种情况时遇到了困难。我尝试阅读重绘系统,但我无法确定为什么在设置其他文本标签时需要重绘非重叠表格。
我使用 Swing Explorer 尝试可视化布局边框,以确保事物不会以某种疯狂的方式重叠,并且一切看起来都很好。
我正在寻找一种方法来确定要求 JTable 重新绘制的调用顺序。当我在其paintComponent() 上添加断点并向上遍历堆栈时,我最终进入了RepaintManager 的paintDirtyRegions() 调用,其中我的整个主JPanel(包含窗口中的所有内容)位于tmpDirtyComponents 变量内。
谁能解释当我最初只是设置一个状态文本标签时,我的整个 JPanel 如何被标记为脏?如果没有,您能否提供一些有关在何处挂钩以查找错误的指导?
非常感谢您提供的任何帮助!
I'm having some strange issues with repainting going on in a Swing app I'm developing. I'm finding that when I set the text of a certain component (it's just a status bar text label along the very bottom of the window) an entire table in a very different area of the screen is being told to repaint. It's causing some performance issues because it seems like basically the entire window is being asked to repaint every time I set the status bar text.
I've been wandering around the RepaintManager trying to figure out what's going on, and have found that my entire main JPanel is being marked as a dirty component when I try to set the label of this text. However, I'm having trouble debugging at what point that's occurring and exactly why it's occurring at all. I've tried to read up on the repainting systems, but I can't determine why it would be necessary for a non-overlapping table to repaint when some other text label is set.
I've used Swing Explorer to try to visualize the layout borders to make sure things aren't overlapping in some crazy way, and everything appears to be fine.
I'm looking for an approach to determine what sequence of calls is asking the JTable to repaint. When I add a breakpoint on its paintComponent() and walk up the stack, I end up inside the RepaintManager's paintDirtyRegions() call in which my entire main JPanel (that contains everything in the window) is sitting inside the tmpDirtyComponents variable.
Can anybody explain how my entire JPanel could get marked as dirty when I'm initially just setting a single status text label? If not, could you provide some guidance as to where to hook in to find the error?
Thanks so much for any assistance you can provide!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
每当您更改 Swing 组件上的属性时,都会在该组件上调用 revalidate() 和 repaint()。设置文本可能会导致组件的首选大小发生变化,因此需要调用布局管理器并重新绘制整个面板。
我不知道如何解决这个问题。
Whenever you change a property on a Swing component revalidate() and repaint() are invoked on the component. Setting the text can cause the preferred size of the components to change so therefore the layout manager needs to be invoked and the entire panel repainted.
I'm not sure how to get around this.
不知何故,我通过更改布局管理器设法解决了这个问题。我的主面板使用的是 GroupLayout。由于整个 GroupLayout 非常简单,我决定尝试使用 BorderLayout,将 JPanel 中的主窗口内容添加到 BorderLayout.CENTER,并将状态栏标签添加到 BorderLayout.PAGE_END。它修复了当我设置状态标签文本时整个窗口的奇怪失效和重绘!感谢大家提出的宝贵建议!
Somehow I've managed to skirt this issue by changing my layout manager. My main panel was using a GroupLayout. Since the overall GroupLayout was so simple I decided to try a BorderLayout instead, with the main window contents in a JPanel added to BorderLayout.CENTER and the status bar label at BorderLayout.PAGE_END. It fixes the weird invalidation and redrawing of the whole window when I set the status label text! Thanks to everybody for their great suggestions!
只是添加到 camickr 的评论中,针对麻烦的组件重写 isValidateRoot() 是此问题的正确解决方案,并且通常是解决不必要的重绘问题的正确解决方案。
缺点是,如果对 isValidateRoot 组件的更改实际上需要组件更改大小,则您需要手动强制重新验证(myValidateRoot.getParent().invalidate() 应该执行此操作)。
Just to add to camickr's comment, overriding isValidateRoot() for the troublesome component is the correct solution to this problem, and is usually the correct solution to solving unnecessary repaint problems.
The drawback is that if a change to your isValidateRoot component does actually require the component to change size, you will need to force a revalidation manually (myValidateRoot.getParent().invalidate() should do it).