Java:如何在 ScrollPane 视口上绘制非滚动叠加层? [英] Java: How to draw non-scrolling overlay over ScrollPane Viewport?

查看:17
本文介绍了Java:如何在 ScrollPane 视口上绘制非滚动叠加层?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我认为(但尚未确认)ScrollPane 实现有效地处理了 View 的呈现(来自后备存储,不必为每个新的部分曝光重新绘制整个 View(甚至 ViewPort)),因此不希望必须重写paint()方法.

我看过 LayerPane,虽然没有掌握它,但似乎这是一种蛮力方法.ScrollPane 是 SplitPane 的一个组件,将被移动/调整大小.我希望我必须使用绝对定位手动维护 ViewPort 和 LayerPane 之间的正确关系.我远非 GUI 设计专家实施,我确信我错过了经验丰富的人会知道的从明显到晦涩和优雅的可能性.

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

谢谢.

更新:

谢谢你的链接,垃圾神.这比我预期的要容易得多.不需要 LayerPane、GlassPane、基于 xor 的合成......我不知道为什么我没有想到尝试覆盖 JViewport.paint() 来绘制我的叠加层 - 但是使用下面的代码我验证了这个概念将给我我正在寻找的东西.只需使用 JViewport 的子类,它就会做我想要的(在这种情况下,当图像在下方滚动时,在视口的中心覆盖一个矩形).我不是 GUI 专家,Java API 非常广泛;我不知道你们是如何记住这一切的 - 但谢谢!

class MyViewport 扩展 JViewport {@覆盖公共无效油漆(图形g){super.paint(g);Graphics2D g2 = (Graphics2D)g;g2.setPaint(颜色.黑色);g2.drawRect(getWidth()/4,getHeight()/4,getWidth()/2,getHeight()/2);}}

解决方案

通常,Swing 程序应该覆盖 paintComponent() 而不是覆盖 paint()",如) 作为滚动窗格的视图会产生奇怪的视觉错误,例如滚动时移动固定的蓝色框.在视图组件上使用 setOpaque(false) 来解决这个问题.

I'd like to use a ScrollPane to display an image in its Viewport, and also have a grid (or box, or any other type of registration/location marker) overlay on the image. I need the overlay to remain fixed when scrolling (meaning the image seems to move "under" the overlay). I will be scrolling the View in the Viewport at a fixed rate to provide a smooth motion, and the overlay is to provide a reference to a certain location within the Viewport. Conceptually, think of a large map scrolling in a Viewport, and the Viewport having a rectangle that does not move (relative to the Viewport itself) that marks a region that can be zoomed into based on some user action.

I presume (but have not yet confirmed) that the ScrollPane implementation handles rendering of the View efficiently (from backing store, not having to repaint the entire View (or even ViewPort) for each new partial exposure) and so would prefer not to have to Override the paint() method.

I've looked at LayerPane, and while not having mastered it, it seems that this is a brute force approach. The ScrollPane is one Component of a SplitPane and will be moved/resized. I expect that I'd have to manually maintain the correct relationship between the ViewPort and the LayerPane using absolute positioning. I'm far from an expert in GUI design & implementation and I'm sure I'm missing possibilities ranging from obvious to obscure and elegant that an experienced person would know.

I'm open to any suggestions, not just the path I've started down. Should I (can I) add a JPanel as one component in my SplitPane, then add a LayerPane to that containing my ScrollPane and the overlay (another JPanel) on different layers on the LayerPane? Is there functionality in ScrollPane to support this directly? I've looked at the Java Swing Tutorials and the API documentation but haven't seen anything yet.

Thanks.

UPDATE:

Thanks for the link, trashgod. This was much easier than I had expected. No need for LayerPane, GlassPane, xor-based compositing... I don't know why it didn't occur to me to try overriding JViewport.paint() to draw my overlay - but with the code below I validated the concept will give me what I'm looking for. Just use the subclass of JViewport and it does what I wanted (in this case overlaying a rectangle in the center of the Viewport while the image scrolls underneath). I'm no GUI expert, and the Java API is incredibly wide; I have no idea how you guys keep this all in your head - but thanks!

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

解决方案

Ordinarily, "Swing programs should override paintComponent() instead of overriding paint()," as mentioned in Painting in AWT and Swing: The Paint Methods. Based on ScrollPanePaint, which draws below the scrolling content, the following example overrides paint() to draw above the scrolling content.

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

Note: Setting an opaque component (for instance JTable) as a view for the scroll pane will give strange visual bugs, for instance moving the fixed blue box when scrolling. Use setOpaque(false) on the view component to fix that.

这篇关于Java:如何在 ScrollPane 视口上绘制非滚动叠加层?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆