Java 在paintComponent()中绘制的图形立即丢失

Java 在paintComponent()中绘制的图形立即丢失,java,swing,paintcomponent,Java,Swing,Paintcomponent,我是Java、Swing和GUI编程的新手,因此我可能错过了使用Swing及其背后的线程模型构建GUI的一些要点。我正在尝试的练习包括在画布上创建、移动和调整图形大小的小应用程序。此外,我试图保持视图尽可能无行为,Presenter对象负责注入所需的行为。换句话说,我不希望视图知道图形或图形必须如何绘制,它只是为演示者提供了一个setUpdater()方法,以提供一个对象,该对象知道为了表示模型的状态必须绘制什么 但我发现了一个问题:在某些情况下,数据丢失了。例如,如果我将应用程序窗口图标化,然

我是Java、Swing和GUI编程的新手,因此我可能错过了使用Swing及其背后的线程模型构建GUI的一些要点。我正在尝试的练习包括在画布上创建、移动和调整图形大小的小应用程序。此外,我试图保持视图尽可能无行为,Presenter对象负责注入所需的行为。换句话说,我不希望视图知道图形或图形必须如何绘制,它只是为演示者提供了一个setUpdater()方法,以提供一个对象,该对象知道为了表示模型的状态必须绘制什么

但我发现了一个问题:在某些情况下,数据丢失了。例如,如果我将应用程序窗口图标化,然后取消图标化。我以为画布组件的paintComponent()没有被调用,但断点告诉我问题不同:它被调用,图形被绘制,但随后消失

我试图简化我的代码,在没有按钮、滑块甚至模型的情况下显示问题。但是,我为视图和演示者保留单独的类,因为这种分离对于我的目的很重要

在我测试简化示例的机器中,调用paintComponent时绘制的图形(始终是同一个圆)不仅在去锥化后消失,而且在每次绘制时消失

请帮我了解发生了什么。。。以及如何解决这个问题

蒂娅

PS1:简化代码如下:

import java.awt.*;
import javax.swing.*;

interface ViewUpdater {
    void updateCanvas(Graphics2D g2d);
}

class View {
    private JFrame windowFrame;
    private JPanel canvasPanel;
    private ViewUpdater updater;
    public static final Color CANVAS_COLOR = Color.white;
    public static final int CANVAS_SIDE = 500;

    public View() {
        windowFrame = new JFrame();
        windowFrame.
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        canvasPanel = new JCanvas();
        canvasPanel.setBackground(CANVAS_COLOR);
        canvasPanel.
            setPreferredSize(new Dimension(CANVAS_SIDE,
                                           CANVAS_SIDE));
        windowFrame.getContentPane().add(canvasPanel);
        windowFrame.pack();
        windowFrame.setResizable(false);
    }

    public void setVisible() {
        windowFrame.setVisible(true);
    }

    public void setUpdater(ViewUpdater updater) {
        this.updater = updater;
    }

    public void updateView() {
        System.out.println("BEGIN updateView");
        Graphics2D g2d =(Graphics2D) canvasPanel.getGraphics();
        g2d.setColor(CANVAS_COLOR);
        g2d.fillRect(0, 0, CANVAS_SIDE, CANVAS_SIDE);
        if (updater != null) {
            System.out.println("GOING TO updateCanvas");
            updater.updateCanvas(g2d);
        }
        System.out.println("END updateView");
   }

    private class JCanvas extends JPanel {

        private static final long serialVersionUID =
                                    7953366724224116650L;

        @Override
        protected void paintComponent(Graphics g) {
            System.out.println("BEGIN paintComponent");
            super.paintComponent(g);
            updateView();
            System.out.println("END paintComponent");
        }
    }
}

class Presenter {
    private View view;
    private static final Color FIGURE_COLOR = Color.black;

    public Presenter(View view) {
        this.view = view;
        this.view.setUpdater(new ProjectViewUpdater());
        this.view.setVisible();
    }

    private class ProjectViewUpdater
        implements ViewUpdater {
        @Override
        public void updateCanvas(Graphics2D g2d) {
            g2d.setColor(FIGURE_COLOR);
            g2d.drawOval(100, 100, 300, 300);
            // The circle immediately disappears!
        }
    }
}

public class Main {
    public static void main(String[] args) {
        new Presenter(new View());
    }
}
PS2:我阅读本书是为了理解使用Swing所涉及的线程模型,但我仍然没有在代码中做任何控制线程的特殊工作


PS3:我在谷歌搜索时没有找到问题的答案,最类似的问题可能是中没有回答的问题。

您的问题是,您通过在JPanel上调用
getGraphics()
来获取图形对象,这样获得的任何图形对象都不会持久,这样,用它绘制的任何东西在下次重新绘制时也会消失

解决办法:不要这样做。或者

  • 使用JVM或指定给paintComponent方法的图形对象绘制
  • 在BuffereImage上调用
    getGraphics()
    createGraphics()
    ,以获取其图形或Graphics2D对象,使用此对象绘制,处理它,然后使用JVM传入的图形对象再次在JComponent或JPanel的
    paintComponent(…)
    方法中绘制BuffereImage

对于您的情况,我认为您最好使用BuffereImage或上面的第2点。

我看这里没有什么问题,事实上,我运行了这个程序,它工作得非常好。你确定你看到了这个简化版本的问题吗?顺便说一句,这个关联的问题与你的问题完全不同——它与SWT内部嵌入的Swing有关,SWT是一个完全不相关的窗口工具包,它本身的一系列问题在技术上也很重要。@ernest friedman hill:你真的看到Deiconicion之后的圆圈了吗?我管理Eclipse中的代码,并“作为Java应用程序”运行它,这会有什么不同吗?。在我的Windows Vista中,圆圈最初没有显示(至少在相当长的一段时间内没有显示),在将窗口从部分外部移到桌面内部时出现,并且仅在去锥化后丢失。然而,在我的Ubuntu中,圆圈从未正确显示。我在MacOSX上运行这个,圆圈从未消失。我认为@hovercraftfullofels可能是对的,解决方案是将JCanvas.paintComponent()作为
g
的参数传递到
updateView()
,然后它应该使用该参数,而不是调用getGraphics()。@ernest friedman hill:感谢您提供有关Mac OS X下行为的信息。非常感谢。我已经成功尝试了您提出的第一种方法:图形g现在从paintComponent()传递到updateView(),在这里它代替了canvasPanel.getGraphics()。对我来说,这就像魔术。是时候搜索有关getGraphics()和BuffereImage:-)的信息了,任何指针都可以。顺便说一句,你为什么推荐第二个选项?@federico.prat:如果某些内容是静态的,
BufferedImage
可以呈现一次,然后通过
drawImage()
重复复制+1.