【问题标题】:Stuff drawn by paintComponent() disappear after calling repaint()paintComponent() 绘制的东西在调用 repaint() 后消失
【发布时间】:2020-05-22 17:15:42
【问题描述】:

在 Head First 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

【问题讨论】:

  • 假设你是从 EDT 运行的(如果你还没有的话,你应该这样做),你肯定不想在摇摆应用程序中使用 Thread.sleep()。相反,请使用摇摆 Timer 制作动画。
  • 还没学过线程。这是防止椭圆形涂抹的原因吗?
  • @vader,不,线程是一个单独的命令执行序列。想想几个处理器内核,每个内核都有自己必须运行的命令列表(但在 Java 中,它是模拟和调度的,而且......你明白了)。大多数 Java 应用程序都有多个,尤其是 Swing 有一个称为“UI 线程”的东西,它负责绘制应用程序,理想情况下你不应该让它休眠,因为这样 UI 就会变得无响应。
  • 您发布的代码不完整。我们不知道 go() 方法是如何被调用的,所以我们不知道您的代码是否在 EDT 上执行。如果代码在 EDT 上执行,那么您会导致 EDT 进入睡眠状态,这意味着框架无法自行重绘。如果它不在 EDT 上,那么您应该看到圆圈的“涂抹”。但如果没有可执行代码,我们无法确定您在做什么。
  • 别告诉我们。发布您正在执行的确切代码。所以我们可以复制/粘贴/编译/文本。这称为minimal reproducible example。每个问题都应该发布一个“MRE”。

标签: java swing jpanel paintcomponent


【解决方案1】:

这是防止椭圆形涂抹的原因吗?

class MyDrawPanel extends JPanel {
    public void paintComponent(Graphics g) {
        g.setColor(Color.orange);
        g.fillOval(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 上执行。

【讨论】:

  • 不添加 super 调用,绘制的椭圆会被自动移除。我想知道如何保留它们,因为这本书说我的代码应该保留所有以前绘制的椭圆,并且看起来像在屏幕上绘制了一条对角粗线。为什么现在不行?
  • 您所描述的与应该发生的相反。发布您的minimal reproducible example
  • 当我运行你的代码时,我在屏幕上看到了椭圆的“涂抹”,因为正如预期的那样,背景没有被清除。如果您添加我在上面建议的语句,则背景将被清除,您只会看到一个椭圆形。我在 Windows 7 上使用 JDK8。如果您遇到问题,可能是您的平台。
  • 我尝试添加 isEventDispatchThread() 并打印“false”。我正在使用 Java 13.0.2 和 OSX Catalina
  • 那么你应该看到我们其他人看到的圆圈的“涂片”(这是正确的)。你还没有明确你的平台。也许使用您平台的其他人需要运行代码。将您的版本/平台添加到问题中,并希望其他人回答问题。
猜你喜欢
  • 2016-05-20
  • 2015-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-03
  • 1970-01-01
  • 2015-03-30
  • 1970-01-01
相关资源
最近更新 更多