为什么 repaint() 并不总是有效?

Why repaint() not always work?

在我的第一个程序中重绘工作正常,但在第二个程序中我遇到了问题。

    import java.awt.Graphics;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;
    import javax.imageio.ImageIO;
    import javax.swing.JFrame;
    import javax.swing.JPanel;

    public class repaint1 {

        public static void main(String[] args) {
            JFrame win = new JFrame("");
            test1 content = new test1();
            win.setContentPane(content);
            win.setSize(600, 400);
            win.setLocation(100, 100);
            win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            win.setResizable(false);
            win.setVisible(true);
            content.function();

        }

    }

    class test1 extends JPanel {

        private BufferedImage img;

        public int x = 50;
        public int y = 50;

        public test1() {

        }

        public void function() {

            try {

                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }

            for (int i = 50; i < 150; i++) {
                x = i;
                y = i;

                try {

                    Thread.sleep(10);
                } catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                }

                repaint();
            }
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            try {
                img = ImageIO.read(new File("images.jpg"));
                g.drawImage(img, x, y, null); 

            } catch (IOException ex) {
                // handle exception...
            }

        }

    }



import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class repaint2 {

    public static void main(String[] args) {

        JFrame f = new JFrame("JFrame");
        test2 content = new test2();
        f.setContentPane(content);
        f.setSize(600, 400);
        f.setLocation(100, 100);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setResizable(false);
        f.setVisible(true);

    }

}

class test2 extends JPanel {

    private BufferedImage img;

    public int x = 50;
    public int y = 50;

    public test2() {

        JButton button = new JButton("Start !");
        button.setBounds(458, 24, 122, 23);
        button.setVisible(true);
        add(button);

        button.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                // Execute when button is pressed

                function();

            }

        });

    }

    public void function() {

        for (int i = 50; i < 150; i++) {

            x = i;
            y = i;

            repaint();

            try {

                Thread.sleep(10);
            } catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }

        }
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        try {
            img = ImageIO.read(new File("images.jpg"));
            g.drawImage(img, x, y, null);

        } catch (IOException ex) {
            // handle exception...
        }

    }

}

每次需要在 JPanel 中使用 Runnable 时移动图像 class。这里 class (MyJPanleClass) 的行为与 JPanel 和 Thread 一样。

public class MyJPanleClass extends JPanel implements Runnable{

   public MyJPanleClass(){
      //constractor
   }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        //do your staff with g
    }

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

}

要从 main class 执行它,只需编写以下代码:

MyJPnaleClass jp=new MyJPanelClass();
Thread t1=new Thread(jp);
t1.start();//this will call run class
try{
t1.join();//running the thread until it finish. 
}catch(Exception ex){}

如果您对线程一无所知,请按照 link 进行学习:Thread in java tutorial

在第一种情况下,function() 方法是从主线程调用的,因此当您调用 sleep() 时,该线程会休眠,这不会影响 GUI 的绘制。

在第二种情况下,您的 function() 方法是从 ActionListener 调用的。从侦听器执行的所有代码都在事件调度线程 (EDT) 上调用,该线程负责重新绘制 GUI。当您告诉线程 sleep() 时,EDT 会休眠,因此在循环执行完成之前它无法重新绘制 GUI。

在您的 ActionListener 代码中,您需要启动一个单独的线程。一种方法是使用 SwingWorker.

阅读 Concurrency 上的 Swing 教程部分了解更多信息。