Java 调用repaint()后,paintComponent()绘制的内容将消失

Java 调用repaint()后,paintComponent()绘制的内容将消失,java,swing,jpanel,paintcomponent,Java,Swing,Jpanel,Paintcomponent,在第一本Java书的开头,我们看到了一些小动画,我试图画一个画对角线的动画。我使用paintComponent()方法在x,y处绘制一个椭圆形(每次循环时都会更新该值)。为什么我会丢失先前绘制的椭圆?根据这本书,我应该在屏幕上得到一个涂片,在那里以前画的椭圆没有丢失。这需要通过在每次调用repaint()时向paintComponent()方法添加一个白色背景来解决,但我没有得到“错误”这是为什么?如何在面板上保留先前绘制的椭圆形?使用JDK 13.0.2和Mac OSX Catalina im

在第一本Java书的开头,我们看到了一些小动画,我试图画一个画对角线的动画。我使用paintComponent()方法在x,y处绘制一个椭圆形(每次循环时都会更新该值)。为什么我会丢失先前绘制的椭圆?根据这本书,我应该在屏幕上得到一个涂片,在那里以前画的椭圆没有丢失。这需要通过在每次调用repaint()时向paintComponent()方法添加一个白色背景来解决,但我没有得到“错误”这是为什么?如何在面板上保留先前绘制的椭圆形?
使用JDK 13.0.2和Mac OSX Catalina

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

public class SimpleAnimation {

    int x = 70;
    int y = 70;

    public static void main(String[] args) {
        SimpleAnimation gui = new SimpleAnimation();
        gui.go();
    }

    public void go() {
        JFrame frame = new JFrame();
        MyDrawPanel drawPanel = new MyDrawPanel();

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.getContentPane().add(drawPanel);
        frame.setSize(300,300);
        frame.setVisible(true);

        for (int i = 0; i < 130; i++) {
            x++;
            y++;
            drawPanel.repaint();
            try {
                Thread.sleep(25);
            } catch (Exception ex){};

        }
    }
    class MyDrawPanel extends JPanel {

        public void paintComponent(Graphics g) {
            g.setColor(Color.orange);
            g.fillOval(x,y,50,50);
        }
    } // close inner class
} // close outer class

import javax.swing.*;
导入java.awt.*;
公共类简化{
int x=70;
int y=70;
公共静态void main(字符串[]args){
SimpleAnimation gui=新建SimpleAnimation();
gui.go();
}
公开作废go(){
JFrame=新JFrame();
MyDrawPanel drawPanel=新建MyDrawPanel();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(drawPanel);
框架。设置尺寸(300300);
frame.setVisible(true);
对于(int i=0;i<130;i++){
x++;
y++;
drawPanel.repaint();
试一试{
睡眠(25);
}捕获(例外情况除外){};
}
}
类MyDrawPanel扩展了JPanel{
公共组件(图形g){
g、 setColor(颜色为橙色);
g、 椭圆形(x,y,50,50);
}
}//关闭内部类
}//关闭外部类
这就是防止卵子涂片的原因吗

代码应为:

class MyDrawPanel extends JPanel {
    public void paintComponent(Graphics g) {
        super.paintComponent(g); // added
        g.setColor(Color.orange);
        g.fillOval(x,y,50,50);
    }
在进行自定义绘制之前,需要使用
super.paintComponent(g)
清除面板的背景


是的,很棒的书。我得到的代码输出是“更正版本”

编辑:

这本书是正确的。你需要了解EDT是如何工作的。启动应用程序时,main()方法中调用的代码在单独的线程上执行。Swing事件和绘制在事件调度线程(EDT)上完成。因此,在go()方法中调用sleep()不应影响圆的绘制,您应该看到涂抹。如果在循环完成后只看到一个椭圆形,则这意味着您的IDE或平台在EDT上的main()方法中启动代码,这是不正常的

您可以通过添加以下内容来验证我的上述声明:

System.out.println( SwingUtilities.isEventDispatchThread() );

查看它是否在EDT上执行。

嘿,我看你也读过Head First Java的书,不错吧

当我在同一本书中学习第12章时,我也遇到了同样的问题

在第15章学习了线程之后,我明白了为什么椭圆不会在屏幕上“涂抹”

首先,关于
repaint()
方法

调用
repaint()
方法不会立即调用JPanel中的
paintComponent(Graphics g)
。相反,它会安排对事件调度线程的调用以供以后使用

我不确定你得到了什么输出,因为你没有告诉我们

我假设根本没有动画。当你运行它时,在终端屏幕上可能只有一个椭圆形

如果是这样,程序就是这样运行的。循环运行了150次,因此调用了
repaint()
150次。但是,正如我前面所说,
repaint()
只调用事件调度线程中的计划,而不是立即运行
paintComponent(Graphics g)

这意味着for循环在实际调用
paintComponent(Graphics g)
之前完成。这很可能就是你没有在书中看到“错误”的原因

你怎么知道“错误”


尝试使线程睡眠更长时间。

假设您是从EDT运行的(如果您还没有,则应该这样做),您肯定不想在swing应用程序中使用
thread.sleep()
。相反,使用swing
计时器进行动画。尚未了解线程。这就是防止椭圆涂抹的原因吗?@vader,不,线程是一个单独的命令执行序列。想想几个处理器内核,每个内核都有自己必须运行的命令列表(但在Java中,它是模拟和调度的,而且……你明白了)。大多数Java应用程序都有多个这样的应用程序,Swing尤其有一个叫做“UI线程”的东西,它负责绘制应用程序,理想情况下,您永远不应该让它休眠,因为这样UI就会变得无响应。您发布的代码不完整。我们不知道go()方法是如何调用的,所以我们不知道您的代码是否在EDT上执行。如果代码在EDT上执行,则会导致EDT休眠,这意味着帧无法重新绘制自身。如果它不在EDT上,那么你应该看到圆圈的“涂抹”。但是如果没有可执行代码,我们无法确定您在做什么。不要告诉我们。张贴您正在执行的确切代码。所以我们可以复制/粘贴/编译/文本。这种方法被称为。每个问题都应该贴上“MRE”。不添加超级调用,绘制的椭圆将自动删除。我想知道如何保存它们,因为书中说我的代码应该保存所有以前绘制的椭圆,并且看起来就像在屏幕上绘制了一条粗对角线。为什么不是现在?你所描述的与应该发生的相反。发布你的代码。当我运行你的代码时,我在屏幕上看到椭圆形的“涂抹”,因为正如预期的那样,背景没有被清除。如果你加上我在上面建议的陈述,那么背景就会被清除,你只能看到一个椭圆形。我在Windows7上使用JDK8。如果您遇到问题,可能是您的平台造成的。我尝试添加isEventDispatchThread()并
System.out.println( SwingUtilities.isEventDispatchThread() );