简体   繁体   English

调用 repaint() 后,paintComponent() 绘制的东西消失了

[英]Stuff drawn by paintComponent() disappear after calling repaint()

On Head First Java book we're seeing some little animation and I'm trying to draw an animation that traces out a diagonal line.在 Head First Java 书中,我们看到了一些小动画,我正在尝试绘制一个描绘对角线的动画。 I'm doing so by using the paintComponent() method and drawing an oval at x,y (values which are updated each time through the loop).我这样做是通过使用paintComponent() 方法并在x,y 处绘制一个椭圆(每次通过循环更新的值)。 Why do I lose the previously drawn ovals?为什么我丢失了之前绘制的椭圆? According to the book I should be getting a smear on the screen where the previously drawn ovals are not lost.根据这本书,我应该在屏幕上得到一个涂抹,先前绘制的椭圆不会丢失。 This should need fixing by adding to the paintComponent() method a white background every time repaint() is called but instead i'm not getting the 'mistake'.这应该需要通过在每次调用 repaint() 时向 paintComponent() 方法添加白色背景来修复,但我没有得到“错误”。 Why is this and how do I get to KEEP the previously drawn ovals on the panel?为什么会这样,我如何才能在面板上保留先前绘制的椭圆?
Using JDK 13.0.2 and Mac OSX Catalina使用 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

Is that what prevents the smear of ovals?这是防止椭圆形涂抹的原因吗?

class MyDrawPanel extends JPanel {
    public void paintComponent(Graphics g) {
        g.setColor(Color.orange);
        g.fillOval(x,y,50,50);
    }

The code should be:代码应该是:

class MyDrawPanel extends JPanel {
    public void paintComponent(Graphics g) {
        super.paintComponent(g); // added
        g.setColor(Color.orange);
        g.fillOval(x,y,50,50);
    }

You need the super.paintComponent(g) to clear the background of the panel before doing the custom painting.在进行自定义绘画之前,您需要super.paintComponent(g)来清除面板的背景。

Yeah, great book.是的,很棒的书。 The output i'm getting with the code as is, is the "corrected version"我用代码得到的输出是“更正版本”

Edit:编辑:

The book is correct.书是对的。 You need to understand how the EDT works.您需要了解 EDT 的工作原理。 When you start an application the code invoked in the main() method executes on a separate Thread.当您启动应用程序时,在 main() 方法中调用的代码在单独的线程上执行。 Swing events and painting is done on the Event Dispatch Thread (EDT). Swing 事件和绘制是在事件调度线程 (EDT) 上完成的。 So invoking sleep() in the go() method should NOT affect the painting of the circle and you should see the smear.所以在 go() 方法中调用 sleep() 不应该影响圆圈的绘制,你应该看到涂抹。 If you only see a single oval painted after the loop is finished, then this imples your IDE or platform starts the code in the main() method on the EDT, which is not normal.如果在循环完成后只看到一个椭圆形,那么这意味着您的 IDE 或平台在 EDT 上的 main() 方法中启动代码,这是不正常的。

You can verify my above statement by adding:您可以通过添加以下内容来验证我的上述声明:

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

to your loop to see if it is executing on the EDT or not.到您的循环以查看它是否在 EDT 上执行。

Hey I see you've read the Head First Java book too, good book isn't it?嘿,我知道你也读过 Head First Java 的书,是不是好书?

I've had the same problem when I was learning about chapter 12 in the same book.我在学习同一本书的第 12 章时遇到了同样的问题。

After learning about threads in chapter 15, I figured why the ovals don't "smear" across the screen.在第 15 章中学习了线程之后,我明白了为什么椭圆不会“涂抹”在屏幕上。

So first of all, about the repaint() method.首先,关于repaint()方法。

Calling the repaint() method doesn't instantly call paintComponent(Graphics g) in the JPanel.调用repaint()方法不会立即调用paintComponent(Graphics g) Instead, it schedules the call to the Event Dispatch Thread for later use.相反,它调度对事件调度线程的调用以供以后使用。

I'm not sure what output you are getting since you didn't tell us about it.我不确定你得到了什么输出,因为你没有告诉我们。

I'm assuming that there is no animation at all.我假设根本没有动画。 There's probably just one oval in the end screen when you run it.当您运行它时,最终屏幕中可能只有一个椭圆形。

If that's the case, this is how the program is running.如果是这样,这就是程序运行的方式。 You run the loop 150 times so you called repaint() 150 times.您运行了 150 次循环,因此您调用了repaint() 150 次。 However, like I said before, repaint() only calls a schedule in the Event Dispatching Thread, instead of running paintComponent(Graphics g) instantly.然而,就像我之前说的, repaint()只在事件调度线程中调用一个调度,而不是立即运行paintComponent(Graphics g)

That means the for loop is finished before the paintComponent(Graphics g) actually got called.这意味着 for 循环在paintComponent(Graphics g)实际被调用之前完成。 That's most likely why you are not getting "The Mistake" in the book.这很可能就是你没有在书中得到“错误”的原因。

How do you get "The Mistake"?你如何获得“错误”?

Try making the thread sleep for a longer time.尝试让线程休眠更长时间。

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

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