Java:如何在滚动窗格视口上绘制非滚动覆盖?

Java:如何在滚动窗格视口上绘制非滚动覆盖?,java,swing,graphics,overlay,scrollpane,Java,Swing,Graphics,Overlay,Scrollpane,我希望使用滚动窗格在其视口中显示图像,并在图像上覆盖网格(或框,或任何其他类型的注册/位置标记)。我需要覆盖层在滚动时保持固定(这意味着图像似乎在覆盖层下移动)。我将在视口中以固定速率滚动视图,以提供平滑的运动,叠加将提供对视口中某个位置的引用。从概念上讲,设想一个在视口中滚动的大型地图,并且该视口具有一个不移动的矩形(相对于视口本身),该矩形标记了一个可以根据某些用户操作放大的区域 我假定(但尚未确认)ScrollPane实现可以有效地处理视图的渲染(从备份存储,不必为每个新的部分曝光重新绘制

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

我假定(但尚未确认)ScrollPane实现可以有效地处理视图的渲染(从备份存储,不必为每个新的部分曝光重新绘制整个视图(甚至视口),因此我更希望不必重写paint()方法

我看过LayerPane,虽然还没有掌握它,但这似乎是一种蛮力方法。滚动窗格是拆分窗格的一个组件,将被移动/调整大小。我希望我必须使用绝对定位手动维护视口和图层平面之间的正确关系。我远非GUI设计和实现方面的专家,我确信我错过了一些可能性,从显而易见到晦涩难懂,一个有经验的人都会知道

我愿意接受任何建议,而不仅仅是我开始走的路。我是否应该(可以)在拆分窗格中添加一个JPanel作为一个组件,然后在其中添加一个LayerPane,其中包含我的滚动窗格和LayerPane上不同层上的覆盖层(另一个JPanel)?ScrollPane中是否有直接支持此功能的功能?我已经看过JavaSwing教程和API文档,但还没有看到任何内容

谢谢

更新:

谢谢你的链接,垃圾神。这比我想象的要容易得多。不需要分层窗格、玻璃窗格、基于xor的合成。。。我不知道为什么我没有想到尝试覆盖JViewport.paint()来绘制覆盖图-但是通过下面的代码,我验证了这个概念将为我提供我想要的东西。只需使用JViewport的子类,它就完成了我想要的(在本例中,在视口中心覆盖一个矩形,而图像在下方滚动)。我不是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()
”,如中所述。基于在滚动内容下方绘制的,下面的示例将覆盖
paint()
以在滚动内容上方绘制

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

import java.awt.*;
导入javax.swing.*;
/**
*@见https://stackoverflow.com/a/10097538/230513
*@见https://stackoverflow.com/a/2846497/230513
*@见https://stackoverflow.com/a/3518047/230513
*/
公共类ScrollPanePaint扩展了JFrame{
专用静态最终整块=64;
公共ScrollPanePaint(){
JViewport viewport=newmyviewport();
setView(新的MyPanel());
JScrollPane scrollPane=新的JScrollPane();
scrollPane.setViewport(视口);
添加(滚动窗格);
此.setDefaultCloseOperation(在关闭时处理);
这个包();
此.setLocationRelativeTo(空);
此.setVisible(true);
}
私有静态类MyViewport扩展了JViewport{
公共MyViewport(){
此.set不透明(false);
此.setPreferredSize(新尺寸(6*TILE,6*TILE));
}
@凌驾
公共空间涂料(图g){
超级油漆(g);
g、 setColor(Color.blue);
g、 fillRect(瓷砖,瓷砖,3*瓷砖,3*瓷砖);
}
}
私有静态类MyPanel扩展了JPanel{
公共事务委员会(){
此.set不透明(false);
此.setPreferredSize(新尺寸(9*TILE,9*TILE));
}
@凌驾
受保护组件(图形g){
超级组件(g);
g、 setColor(颜色:浅灰色);
int w=this.getWidth()/TILE+1;
int h=this.getHeight()/TILE+1;
对于(int行=0;行

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

另请参见。更新:另见。两者中的一个或两个都可以帮助你构建一个链接。这个链接正是我在正确方向上所需要的推动。用解决方案更新了问题。谢谢垃圾神。感谢@ags提出的这种方法。@mKorbel:如果我没有遵守这里的协议,我很抱歉。是否有一些建议或问题我遗漏了?我无意中提到了paint()和paintComponent(),但我需要做一些研究来理解它们之间的区别以及它们在什么时候合适。再次感谢您的指点。@ags:抱歉,我们的讨论时间太长了;我珍视mKorbel的经验。不在
JComponent
中重写
paint()
的一个原因是它破坏了轻量级的前后渲染顺序。在这种情况下,这是一个功能。