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); }