Java:如何在ScrollPane Viewport上绘制非滚动叠加层?

我想使用ScrollPane在其视口中显示图像,并在图像上还有一个网格(或框,或任何其他类型的注册/位置标记)覆盖。 滚动时我需要叠加层保持固定(意味着图像似乎在叠加层下“移动”)。 我将以固定速率滚动视口中的视图以提供平滑运动,并且叠加层将提供对视口内某个位置的引用。 从概念上讲,考虑在视口中滚动的大地图,以及具有不移动的矩形(相对于视口本身)的视口,该矩形标记可以基于某些用户动作放大的区域。

我假设(但尚未确认)ScrollPane实现有效地处理View的渲染(从后备存储,不必为每个新的部分曝光重新绘制整个View(甚至ViewPort)),因此不希望覆盖paint()方法。

我看过LayerPane,虽然没有掌握它,但似乎这是一种蛮力的方法。 ScrollPane是SplitPane的一个组件,将被移动/resize。 我希望我必须使用绝对定位手动维护ViewPort和LayerPane之间的正确关系。 我远不是GUI设计和实现方面的专家,我确​​信我缺少从有经验的人会知道的明显到模糊和优雅的可能性。

我愿意接受任何建议,而不仅仅是我开始的道路。 我是否(我可以)在我的SplitPane中添加JPanel作为一个组件,然后在LayerPane的不同图层上添加一个LayerPane到包含我的ScrollPane和覆盖(另一个​​JPanel)的那个? ScrollPane中是否有function直接支持此function? 我查看了Java Swing教程和API文档,但还没有看到任何内容。

谢谢。

更新:

感谢您的链接,trashgod。 这比我想象的要容易得多。 不需要LayerPane,GlassPane,基于xor的合成……我不知道为什么我没有尝试重写JViewport.paint()来绘制我的叠加层 – 但是使用下面的代码我validation了概念将给我我正在寻找的东西。 只需使用JViewport的子类,它就可以完成我想要的操作(在这种情况下,当图像在下面滚动时,在Viewport的中心覆盖一个矩形)。 我不是GUI专家,Java API非常广泛; 我不知道你们怎么把这一切都放在脑海里 – 但是谢谢!

class MyViewport extends JViewport { @Override public void paint(Graphics g) { super.paint(g); Graphics2D g2 = (Graphics2D)g; g2.setPaint(Color.BLACK); g2.drawRect(getWidth()/4,getHeight()/4,getWidth()/2,getHeight()/2); } } 

通常,“Swing程序应该覆盖paintComponent()而不是覆盖paint() ”,如在AWT和Swing绘画中提到的:绘制方法 。 基于ScrollPanePaint (在滚动内容下方绘制),以下示例覆盖paint()以在滚动内容上方绘制。

ScrollPanePaint

 import java.awt.*; import javax.swing.*; /** * @see https://stackoverflow.com/a/10097538/230513 * @see https://stackoverflow.com/a/2846497/230513 * @see https://stackoverflow.com/a/3518047/230513 */ public class ScrollPanePaint extends JFrame { private static final int TILE = 64; public ScrollPanePaint() { JViewport viewport = new MyViewport(); viewport.setView(new MyPanel()); JScrollPane scrollPane = new JScrollPane(); scrollPane.setViewport(viewport); this.add(scrollPane); this.setDefaultCloseOperation(DISPOSE_ON_CLOSE); this.pack(); this.setLocationRelativeTo(null); this.setVisible(true); } private static class MyViewport extends JViewport { public MyViewport() { this.setOpaque(false); this.setPreferredSize(new Dimension(6 * TILE, 6 * TILE)); } @Override public void paint(Graphics g) { super.paint(g); g.setColor(Color.blue); g.fillRect(TILE, TILE, 3 * TILE, 3 * TILE); } } private static class MyPanel extends JPanel { public MyPanel() { this.setOpaque(false); this.setPreferredSize(new Dimension(9 * TILE, 9 * TILE)); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.lightGray); int w = this.getWidth() / TILE + 1; int h = this.getHeight() / TILE + 1; for (int row = 0; row < h; row++) { for (int col = 0; col < w; col++) { if ((row + col) % 2 == 0) { g.fillRect(col * TILE, row * TILE, TILE, TILE); } } } } } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { new ScrollPanePaint(); } }); } } 

注意:将不透明组件(例如JTable )设置为滚动窗格的视图会产生奇怪的视觉错误,例如在滚动时移动固定的蓝色框。 在视图组件上使用setOpaque(false)来修复它。