【发布时间】:2018-09-02 18:41:32
【问题描述】:
我对 Swing 的了解很差,对于愚蠢的问题,我深表歉意。
我需要做的是通过缓冲区(BufferedImage 实例)执行绘制来制作我的自定义组件(它们是JPanel 的祖先)。这是一项要求,因为绘制过程可能非常繁重,因此 paintComponent 方法被覆盖以从该缓冲区中绘制并立即返回(但如果您知道更好的方法来告诉 Java 不要每秒重绘对象超过 200-300 次消耗所有的 CPU - 我会很感激,也许有一些公共方法可以将图形上下文区域标记为“未更改”,因此 Swing 不会尝试重新绘制它们)。
我的代码有什么问题,它根本没有在重叠的 JPanel 上绘制数据。我已经为问题的核心做了一个可重现的例子。请看下面的代码:
public class Dr extends JPanel {
public Dr(int x, int y, int w, int h, int fw, int fh) {
left = x;
top = y;
width = w;
height = h;
full_width = fw;
full_height = fh;
setOpaque(true);
}
public void draw() {
if (buffer == null && width > 0 && height > 0) {
buffer = new BufferedImage(width, height, java.awt.image.BufferedImage.TYPE_INT_ARGB);
}
if (buffer == null) return;
Graphics g = buffer.getGraphics();
setBounds(0, 0, full_width, full_height);
Graphics2D g2d = (Graphics2D)g;
g2d.setBackground(Color.WHITE);
g2d.clearRect(0, 0, full_width, full_height);
g2d.setColor(new Color(255, 0, 80, 128));
g2d.fillRect(left, top, width, height);
System.out.println(left + ", " + top);
}
@Override
public void repaint() {
draw();
}
@Override
public void paintComponent(Graphics g) {
g.drawImage(buffer, 0, 0, null);
}
private BufferedImage buffer;
private int left;
private int top;
private int width;
private int height;
private int full_width;
private int full_height;
}
这是一个示例类,它只保存要绘制的坐标和完整尺寸(我希望它等于其父尺寸,这又是一个要求,因为我需要支持可见的溢出内容才能成为显示)并绘制一个具有 100% 不透明度一半的红色矩形。
这是使用它的外部代码:
Dr dr1 = new Dr(4, 4, width-10, 80, width-2, height-2);
Dr dr2 = new Dr(4, 88, width-10, 80, width-2, height-2);
root.add(dr1);
root.add(dr2);
dr1.setBounds(0, 0, width-2, height-2);
dr2.setBounds(0, 0, width-2, height-2);
dr1.draw();
dr2.draw();
预计会是什么:
当我删除缓冲区并直接在paintComponent 方法中绘制时显示什么:
不填充背景时显示的内容(此处背景具有系统 L&F 颜色,但我需要元素父级的颜色(在我的示例中为白色)
当我从缓冲区中绘制时显示的内容
最后一个非常有趣。红色矩形从右侧和底部部分切掉,整个图像(包括白色背景)从父面板的(0, 0)坐标处剪裁。第二个对象根本不显示。控制台中的坐标是绝对OK的。
我觉得我必须与 Swing 做一些事情来告诉它不要在猜测要绘制什么和不绘制什么时执行它的“魔法”。但是我该怎么做呢?
更新:
我发现即使我在添加子组件后不调用draw() 方法,它们仍然会隐藏其父组件的背景(这不应该发生,bacase JPanel 是不透明的)。所以核心问题是我不能让我的Dr 对象具有不透明的背景(例如没有背景),其中没有任何内容(这也解释了为什么我只看到第一个矩形 - Swing 中的 z 顺序似乎反转,例如最后添加的组件被绘制在底部,因为它离我们最远)。
【问题讨论】:
-
1.不要像那样覆盖
repaint。系统可能会调用该方法,而您覆盖它的方式违反了 Liskov 替换原则。 2. 在您覆盖paintComponent时调用super.paintComponent(g);。 3. 你在draw中调用setBounds的方式看起来很可疑,但我不知道你的意图。直接在组件上设置边界很少是正确的,当然当调用者只是期望重绘时也不正确。相反,您应该使用布局管理器,然后使用ComponentListener响应系统所做的大小更改(如果需要)。 -
如果您使用 minimal, complete example 更新您的问题,我们可以更轻松地提供帮助,因此我们不必猜测您的其余代码在做什么。
-
“以及你覆盖它的方式违反了 Liskov 替换原则” - 为什么?当系统调用repaint方法时,可以进行全力repaint。这就是我想要的。你认为我会经常重绘而不需要更新缓冲区吗?
-
调用 super.paintComponent(g) 完全没有做任何事情。在我看来,我根本不应该使用 Swing 机制(我正在制作浏览器渲染引擎)。其中一个想法是将图层(
BufferedImage实例)存储在特殊管理器的 Vector 中,并让我的面板按照 z-index 的顺序绘制这些图层。并且根本不要将子块元素添加为其父元素的子元素(作为嵌套的 JPanel) - 无论如何,我通过覆盖事物来破坏 Swing 机制。 -
查看
repaint的文档。你的覆盖做一些不同的事情。您至少需要致电super.repaint(),但您可能不应该在该方法中创建图像和东西。您还应该阅读oracle.com/technetwork/java/painting-140037.html