为什么我从事件队列进行绘画并覆盖paintComponent()时却不绘画/重新绘画我的自定义JPanel?

问题描述 投票:0回答:1

我试图制作我的第一个Swing应用程序,但我终生无法弄清为什么什么都没画,而我却只有一个空的Window。

我正在使用SwingUtilities.invokeLater启动AWT事件队列线程,在我的控制器中,我正在将SwingWorker doInBackground()用于我的主循环,正在处理我的模型代码。

我使用事件将更改从我的模型传递到控制器,然后控制器将其放入并发更改队列中,视图使用该队列来更新自身。

我已经在这里待了几个小时,但仍然不知道为什么它不会画画。我从事件队列线程中绘画,我重写paintComponent并调用super.paintComponent(),就像应该完成的(据我所知)。

sys.outs指示其余的应用程序逻辑似乎正常工作。

我试图将代码缩减为必需品,但这仍然是一堵墙,对此感到抱歉。它应该编译。

import javax.swing.SwingUtilities;

public class Application {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                Controller controller = new Controller("Swing MVC Test Application");
                controller.start();
            }
        });
    }
}

型号

import javax.swing.event.EventListenerList;

public class Model {

    private EventListenerList listeners = new EventListenerList();
    private int[][] someChange = new int[][]{
            { 0,1},
            { 2,3},
            {4,5}
    };
    private int gameLoopCounter = 0;

    public Model(ModelChangeListener listener) {
        listeners.add(ModelChangeListener.class, listener);
    }

    protected synchronized void notifyListeners(ModelChangeEvent event) {
        for (ModelChangeListener l : listeners.getListeners(ModelChangeListener.class))
            l.receiveModelChange(event);
    }

    public void nextGameLoopIteration() {
        notifyListeners(new ModelChangeEvent(this, someChange[gameLoopCounter]));
        gameLoopCounter++;
    }

}

控制器

import javax.swing.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class Controller implements ModelChangeListener {
    private static final int QUEUE_SIZE = 20;

    private Model model;
    private View view;

    public final BlockingQueue<ModelChangeEvent> changeQueue;

    public Controller(String title) {
        this.changeQueue = new ArrayBlockingQueue<ModelChangeEvent>(QUEUE_SIZE) ;
        this.model = new Model(this);
        this.view = new View(this, title);
    }
    public void start() {
        SwingWorker<Void, Void> mainLoop = new SwingWorker<Void, Void>() {
            @Override
            protected Void doInBackground() throws Exception {
                while (true) {
                    if (1 == 0) return null;

                    model.nextGameLoopIteration();
                }
            }
        };
        mainLoop.execute();

        long time = System.currentTimeMillis();
        while (true){
            time += View.MILLIS_PER_LOOP;
            try {
                view.processModelUpdates();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                Thread.sleep(Math.max(0, time - System.currentTimeMillis()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void receiveModelChange(ModelChangeEvent e) {
        try {
            this.changeQueue.put(e);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}



查看

import javax.swing.*;
import java.awt.*;
import java.util.concurrent.TimeUnit;

public class View extends JPanel {
    public static final int MILLIS_PER_LOOP = 1000;
    private static final int CELLSIZE = 40;

    private Controller controller;
    private myPanel myPanel;

    private ModelChangeEvent currentModelState;

    public View(Controller controller, String title) throws HeadlessException {
        this.controller = controller;

        this.setSize(1500,1100);

        initFrame(title);
    }

    private void initFrame(String title) {
        JFrame frame = new JFrame(title);
        frame.setContentPane(this);
        frame.setSize(1600, 1200);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public void processModelUpdates() throws InterruptedException {
        this.currentModelState = this.controller.changeQueue.poll(1, TimeUnit.SECONDS);
        if (currentModelState != null) {
            // do some processing....
            this.repaint();
        }

    }
    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        // draw something
        g.drawOval(currentModelState.getMovementExample()[0],
                currentModelState.getMovementExample()[1],
                CELLSIZE, CELLSIZE);
    }
}  


事件侦听器接口和类

import java.util.EventListener;

public interface ModelChangeListener extends EventListener {
          void  receiveModelChange( ModelChangeEvent e);
}

事件侦听器类

import java.util.EventObject;

public class ModelChangeEvent extends EventObject {
    private int[] movementExample;

    public int[] getMovementExample() {
        return movementExample;
    }

    public ModelChangeEvent(Object source, int[] change) {
        super(source);
        movementExample  = change;
    }

    @Override
    public Object getSource() {
        return super.getSource();
    }
}

java swing paint
1个回答
0
投票

我在主循环中使用SwingWorker doInBackground(),

几件事突出:

model.nextGameLoopIteration();

这基本上是一个无限循环,因为您没有计时机制。对于游戏,通常需要Thread.sleep(…)才能允许游戏重新绘制自身并重置游戏状态。

而且,我实际上看到了第二个循环:

controller.start();

以上代码在EDT上执行。

在具有一会儿(true)循环的start()方法中,您有:

Thread.sleep(Math.max(0, time - System.currentTimeMillis()));

因此,这也是一个无限循环,将导致EDT进入睡眠状态,这意味着它无法响应事件或重新绘制GUI。

不确定为什么有两个循环,但是绝对不应该在EDT上睡觉。

© www.soinside.com 2019 - 2024. All rights reserved.