[英]Java: How to draw non-scrolling overlay over ScrollPane Viewport?
我想使用 ScrollPane 在其视口中显示图像,并在图像上覆盖一个网格(或框,或任何其他类型的注册/位置标记)。 我需要覆盖层在滚动时保持固定(这意味着图像似乎在覆盖层“下方”移动)。 我将以固定的速率在视口中滚动视图以提供平滑的运动,叠加层是为了提供对视口内特定位置的引用。 从概念上讲,想象一个大的 map 在 Viewport 中滚动,并且 Viewport 有一个不移动的矩形(相对于 Viewport 本身),它标记了一个可以根据某些用户操作放大的区域。
我假设(但尚未确认)ScrollPane 实现可以有效地处理视图的渲染(从后备存储,不必为每个新的部分曝光重新绘制整个视图(甚至 ViewPort)),因此我宁愿不必覆盖 paint() 方法。
我看过 LayerPane,虽然没有掌握它,但似乎这是一种蛮力方法。 ScrollPane 是 SplitPane 的一个组件,将被移动/调整大小。 我希望我必须使用绝对定位手动维护 ViewPort 和 LayerPane 之间的正确关系。 我远不是 GUI 设计和实现方面的专家,而且我确信我错过了经验丰富的人会知道的从明显到晦涩和优雅的可能性。
我愿意接受任何建议,而不仅仅是我已经开始的道路。 我是否应该(可以)在我的 SplitPane 中添加一个 JPanel 作为一个组件,然后将一个 LayerPane 添加到包含我的 ScrollPane 的组件中,并在 LayerPane 的不同层上覆盖(另一个 JPanel)? ScrollPane 中是否有直接支持此功能的功能? 我查看了 Java Swing 教程和 API 文档,但还没有看到任何内容。
谢谢。
更新:
感谢您的链接,trashgod。 这比我预期的要容易得多。 不需要 LayerPane、GlassPane、基于 xor 的合成……我不知道为什么我没有想到尝试覆盖 JViewport.paint() 来绘制我的叠加层——但使用下面的代码我验证了这个概念将给我我要找的东西。 只需使用 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()
以在滚动内容上方绘制。
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)
来解决这个问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.