如何使用 swingworker 重新绘制多个图像

How to repaint more than one image with swingworker

我想学习 swing 中的线程。我找到了这个 SwingWorker class 并且我已经编写了简单的代码来移动一些图像。但我仍然有问题。只有一张图像是重新绘制的。当主线程处理它的图像时,第二个线程正在计算图像的位置并且它应该重新绘制。这是Image class的代码(负责更改整数位置和绘图):

public class Image extends JComponent
{
    private BufferedImage image;
    protected int x_location, y_location;
    protected int window_size;
    protected int speed;

    void move_Up()
    {
        this.y_location -= speed;
    }

    void move_Down()
    {
        this.y_location += speed;
    }

    void move_Left()
    {
        this.x_location -= speed;
    }

    void move_Right()
    {
        this.x_location += speed;
    }

    public Image(String directory, int size)
    {
        File file = new File(directory);
        try 
        {
            image = ImageIO.read(file);
        }
        catch (IOException e) 
        {
            e.printStackTrace();
        }

        x_location = 50; 
        y_location = 50;
        speed = 1;
        window_size = size;
        setPreferredSize(new Dimension(50,50));
    }

    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        Graphics2D graphic = (Graphics2D) g;
        graphic.drawImage(image, x_location, y_location, this);
    }
}

扩展 Image 的 WalkingThread 代码,添加由 SwingWorker 处理的函数 运行()。函数负责移动图像:

public class WalkingThread extends Image
{
    boolean directory;

    public WalkingThread(String image, int size) 
    {
        super(image, size);
    }

    void running()
    {
        System.out.println("Repainting");
        this.repaint();
            if( this.y_location == 0) this.directory = true;
            else if( this.y_location+50 == this.window_size) this.directory = false;

            if( this.directory == true ) this.move_Down();
            else if( this.directory == false ) this.move_Up();
            System.out.println(this.y_location+" "+ this.x_location);

            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    }

    public void start()
    {
        System.out.println("I'm in start");
        SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>()
        {
            protected Void doInBackground() throws Exception 
            {
                System.out.println("I'm in doInBackground");
                while(true)
                {
                    running();
                }
            }
        };
        worker.execute();
    }
}

扩展JFrame并实现KeyListener的主框架代码:

public class Window extends JFrame implements KeyListener{

    Image animal;
    WalkingThread MonsterThread;
    int window_Size;

    public Window()
    {
        super("Walking Threads");
        window_Size = 400;
        setSize(window_Size, window_Size);
        setLocation(400, 300);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
        addKeyListener(this);

        MonsterThread = new WalkingThread("kulka.png", window_Size);
        MonsterThread.start();
        add(MonsterThread);

        animal = new Image("kulka.png", window_Size);
        add(animal);
    }

    public void keyPressed(KeyEvent key)
    {
        int pressed = key.getKeyCode();
        if( pressed == KeyEvent.VK_W)
            animal.move_Up();
        else if( pressed == KeyEvent.VK_S)
            animal.move_Down();
        else if( pressed == KeyEvent.VK_A)
            animal.move_Left();
        else if( pressed == KeyEvent.VK_D)
            animal.move_Right();

        animal.repaint();
    }
}

和主要class,处理事件:

public class Main {

    public static void main(String[] args) 
        {
            EventQueue.invokeLater(new Runnable()
            {
                public void run()
                {
                    new Window();
                }
            }); 
        }}

我找不到我的错误。

MosterThread 是唯一真正导致其图像发生任何类型更新的工作人员。该解决方案无法很好地扩展,您添加的图像越多,它就会变得越慢。而不是使用 "walking thread",您应该有一个线程来更新所有实体,安排重绘并在帧之间产生暂停。

注意,Swing 不是线程安全的,你永远不应该在事件调度线程的上下文之外更新 UI,这实际上是 SwingWorker 应该帮助解决的问题,通过提供 processpublish 方法,允许您将数据从后台线程发送到 EDT

查看 Concurrency in Swing 了解更多详情