繁体   English   中英

无法在Swing中正确绘制自定义组件

[英]Can't paint a custom component in Swing correctly

我知道Swing非常差,所以很抱歉这个愚蠢的问题。 我需要做的是使我的自定义组件(它们是JPanel祖先)具有从缓冲区( BufferedImage实例)执行的绘制。 这是一个要求,因为绘制过程可能非常繁琐,所以重写了PaintComponent方法以从该缓冲区绘制并立即返回(但是如果您知道更好的方法来告诉Java,第二次消耗对象的时间不应超过200-300次)所有的CPU-我将不胜感激,也许有一些公共方法可以将Graphics上下文的区域标记为“未更改”,因此Swing不会尝试重新绘制它们)。

我的代码有什么问题,它根本不会在重叠的JPanels上绘制数据。 我已经提出了一个可以重现该问题核心的例子。 请参见下面的代码:

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)坐标中剪切了整个图像(包括白色背景)。 第二个对象根本不显示。 控制台中的坐标绝对可以。

我觉得我与Swing有一些关系,要它在猜测要绘画什么和不绘画什么时告诉它不要执行其“魔术”。 但是我该怎么办呢?

更新:

我发现,即使我在添加子组件之后不对其子集的draw()方法进行调用,它们仍会隐藏其父级的背景(这不应该发生,因为bacase JPanels应该是不透明的)。 因此,核心问题是我无法使我的Dr对象具有不透明的背景(例如,没有背景),其中没有任何内容被绘画(这也解释了为什么我只看到第一个矩形-Swing中的z顺序似乎被反转了) ,例如,最后添加的组件画在底部,因为它离我们最远。

因此,我“认为”您有一个根本性的误解,那就是Swing的所有工作原理。

您似乎对布局管理系统如何工作,绘画系统如何工作以及坐标系如何工作存在问题

让我们从您的Dr组件开始。

您似乎想开发重叠的组件,这些组件可以在其下方显示子组件,但是您可以使用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);
}

对我来说似乎很奇怪。 您将BufferedImage定义为height width ,然后使用full_widthfull_height填充它...在我看来,以另一种方式做起来更有意义

@Override
public void repaint() {
    draw();
}

好的,这是Swing中的重要课程,您无法控制绘画过程,因此请勿尝试。 Swing具有完善的记录和建立良好的绘画过程,可提供定义明确的挂钩,您可以在其中插入自定义绘画(即paintComponent )。 如果需要“控制”,则需要考虑使用BufferStrategy ,它将为您提供完全的控制以定义您自己的绘画过程。

好的,答案是什么?

嗯,这不是那么简单,因为我不确定100%知道我要解决的问题是什么。

但是,让我们从Dr面板开始...

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

        setBounds(x, y, fw, fh);
    }

    public void draw() {
        if (buffer == null && width > 0 && height > 0) {
            buffer = new BufferedImage(getWidth(), getHeight(), java.awt.image.BufferedImage.TYPE_INT_ARGB);
        }
        Graphics g = buffer.getGraphics();
        Graphics2D g2d = (Graphics2D) g;

        g2d.setColor(new Color(255, 0, 80, 128));

        g2d.fillRect(0, 0, width, height);
        g2d.dispose();
    }

    @Override
    public void paintComponent(Graphics g) {
        draw();
        g.drawImage(buffer, 0, 0, this);
        g.setColor(Color.RED);
        g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
    }

    private BufferedImage buffer;

    private int left;
    private int top;
    private int width;
    private int height;
    private int full_width;
    private int full_height;
}

因此,在这里,我对其进行了更改,以便将面板定位在传递给构造函数的xy位置,并将其大小设置为fwfh属性。

然后在draw方法中,我创建一个BufferedImage大小,该大小调整为组件的当前大小并进行绘制...基于widthheight属性的内容...提出了有关为什么必须调整大小的问题,但是在那里是...

然后将缓冲区绘制到组件的顶部/左侧位置,这一点很重要,Swing的坐标系基于组件本身,因此0x0始终是组件的顶部/左侧角,与坐标系无关父容器。

ps-红色矩形用于调试目的,您不需要它。

然后我用...

EventQueue.invokeLater(new Runnable() {
    @Override
    public void run() {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
            ex.printStackTrace();
        }

        JFrame frame = new JFrame("Testing");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setContentPane(new JLayeredPane());
        frame.add(new Dr(10, 10, 180, 180, 200, 200));
        frame.add(new Dr(100, 100, 180, 180, 200, 200));
        frame.setSize(400, 400);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
});

瞧,重叠的组件...

重叠组件

现在,我强烈建议您停下来仔细阅读以下内容:

而且,我的直觉是,您可能真的不想使用单独的组件,而您想要的是一个可以绘制许多缓冲区的组件

我终于解决了这个问题。

问题出在我没有在此处发布的代码中。

我将我的JPanels Dr添加到Block JPanel中。 Block已重写repaint方法以从缓冲区进行绘制,但在当前版本中未重写paintComponent方法。 它还具有方法forceRepaint来重建缓冲区,而方法draw(Graphics g)draw()分别在内部缓冲区或传递的Graphics上下文上进行绘制。 paint方法也没有被覆盖。

结果,在我给孩子add调用之后,Swing在我的Block对象上调用了repaint 但是我的repaint版本只是更新了内部缓冲区,仅此repaint 屏幕上的结果没有任何改变。

因此,这只是一个逻辑错误。

解决方案是在Block类中重新实现paintComponent的重写版本,该类的实例是我在其中添加元素的root对象。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM