java + Swing:矩形或其他“精灵”的有效叠加
我不太清楚怎么说这个,所以请耐心等待。
我在一个带有OverlayLayout
的容器JPanel
有两个JPanel
。 容器中的两个JPanel
都覆盖了paint(Graphics)
。
底部JPanel
是不透明的,并绘制了一些相当复杂的图形,因此渲染需要“很长”的时间(10秒或100毫秒)。
顶部JPanel
是透明的,只是根据鼠标输入绘制一个矩形或线条或简单的形状,所以它真的很快。
有没有办法设置,所以当我更改上面板中的简单形状时,它不会重绘底部面板? (例如它以某种方式缓存底部面板)
我很熟悉w /概念,比如bitblt,double-buffering和XOR-drawing,但不确定在这里应用什么。
您最好使用单个JComponent
并创建BufferedImage
来存储底部图像。 当paintComponent
操作在JComponent
上发生时,您只需将底部图像blit并使用Graphics
对象在其上进行任何进一步绘制(从存储状态)。 应该是相当有效的。
你会想要在另一个post中对底部BufferedImage
进行复杂的绘图操作,正如另一张海报所提到的那样(偶然地省略了这个,对不起:))。 但是,您不希望在此图像上引起争用,因此必须为此存储额外的BufferedImage
,并在绘制操作完成时立即将其同步到另一个图像。
关注复杂的面板,关键是将paintComponent()
除了drawImage()
之外的所有因素。 将其他所有内容放在另一个不断更新屏幕外缓冲区的线程中。 定期更新屏幕,以保持简单面板的响应速度。 唯一困难的部分是同步,但SwingWorker
是一个不错的选择。 还有更多。
可以肯定的是,如果上面板是完整repaint()
目标,那么较低的面板也是。
也许您可以尝试优化区域以在上面板上重新绘制,以避免重新绘制所有较低的区域。 但是如果上面板中的彩绘矩形覆盖整个区域,那么最终会再次完全repaint()
。
通常,Swing尝试优化需要重绘的区域,但是当在短时间内执行多次重绘时它也会聚合这些区域,如果我记得很清楚,聚合区域只是一个矩形,它是所有重绘矩形的并集,并不总是优化,但允许快速计算重绘事件的创建。
现在,我认为你应该遵循先前回复中给出的建议; 实际上,你应该真的避免使用一个paint()
方法,它可以执行那么长的计算(几十秒的ms应该是真正的最大值)。 如果您不希望GUI看起来对最终用户没有响应,那么绘画应该尽可能快。 因此,赞成只执行一次计算(如果可能的话,在EDT之外)将结果存储在BufferedImage
,稍后您只需在paint()
方法中paint()
。
编辑 :添加其他reflection来源
如果要优化点列表的更新但仍保留在paint()
方法中,则可以使用传递的Graphics
的剪切区域来限制对绘图方法的调用,如:
Rectangle clip = g.getClipBounds(); for (Point p: allPoints) { if (clip.contains(p)) { // draw p with g graphics } }
您甚至可以尝试使用QuadTree而不是简单的List
来优化要绘制的点List
,但是您必须自己编写代码(或者找一些免费的实现,其中可能有一些实现)。 使用四叉树,您可以优化时间以查找必须重绘的所有点的列表(基于Graphics
剪切矩形)并仅重绘这些点。
垃圾邮件和jfpoilpret回答的补遗
1 / OverlayLayout是如何布局JPanels的奇怪方法,你是一次JPanel同样的输出(没有OverlayLayout和Translucentcy)
2 /(10s或100s毫秒)可能是小值,因为存在Native OS Latency(今天的OS和PC为45-75ms)
3 /同步将通过在BackGround任务上使用SwingWorker以及绘制流程到JPanel的顺序,方向和同步来管理,也许你的油漆太快/太快
4 /你没有详细描述关于paint()/ paintComponent()的方式,位置和内容
if (SwingUtilities.isEventDispatchThread()) { paintImmediately(int x, int y, int w, int h) // or Rectangle r } else { Runnable doRun = new Runnable() { @Override public void run() { repaint(long tm, int x, int y, int width, int height) // or Rectangle r } }; SwingUtilities.invokeLater(doRun); }