gpt4 book ai didi

Java Swing 多线程管理与观察者模式

转载 作者:行者123 更新时间:2023-12-02 13:39:01 31 4
gpt4 key购买 nike

我的目标是使用观察者模式绘制一个矩形并从左到右平滑地移动它。

我有一个 Model 类,它是可观察的,我在其中放置矩形的坐标,还有一个 Display 类,它是观察者,每次模型中的坐标发生变化时都会执行重新绘制。

模型中的坐标更改是在 SwingWorker 内的 while 循环中进行的:在每次迭代时,我将 x 坐标增加 1,然后 hibernate 100 毫秒,然后通知观察者(显示器)只执行哪个任务重新粉刷。如您所见,在 EDT 上调用了 repaint() 方法,就像建议的那样。

问题是大约一秒后移动不顺畅,重绘频率发生变化,并且似乎矩形的重绘越来越少。

这是模型类:

import java.util.Observable;
import java.awt.EventQueue;
import javax.swing.SwingWorker;

public class Model extends Observable{
int xCoordinate;

Model(Display d){
SwingWorker<Void,Void> sw = new SwingWorker<Void,Void>(){

@Override
protected Void doInBackground() {
while(xCoordinate<600){
xCoordinate ++;

try {
Thread.sleep(100);
} catch (InterruptedException ex) {}
setChanged();
notifyObservers(xCoordinate);
}
return null;
}
};
addObserver(d);
sw.execute();
}

public static void main(String[] a){
EventQueue.invokeLater(new Runnable(){
@Override
public void run(){
Display d = new Display();
Model m = new Model(d);
d.model = m;
}
});
}
}

这是 Display 类:

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Display extends JFrame implements Observer{
Model model;
int xCoordinate;

Display(){
getContentPane().add(new JPanel(){
@Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(xCoordinate, 1, 50, 50);
}
});
setSize(600, 600);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}

@Override
/* arg is the updated xCoordinate*/
public void update(Observable o, Object arg) {
xCoordinate = (Integer)arg;
EventQueue.invokeLater(new Runnable(){
@Override
public void run() {
repaint();
}

});
}
}

我尝试了其他方法,例如使用显示器中的计时器,但这也不起作用。 SwingWorker 在这里可能没用,因为在 SwingWorker 线程上进行的计算很容易(加一),但我需要它来进行我打算在项目(台球游戏)中进行的繁重计算。

我还尝试通过查看两次重绘之间的时间(在显示中)和两次增量之间的时间(在模型中)进行调试,正如预期的那样,每次大约 100 毫秒。

提前致谢

最佳答案

好的,作为初始测试,我从 Swing Timer 开始...

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

public static void main(String[] args) {
new Test();
}

public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}

Model model = new Model();

JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane(model));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Timer timer = new Timer(40, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
model.update();
}
});
timer.setInitialDelay(1000);
timer.start();
}

});
}

public class Model extends Observable {

private int xCoordinate;

public void update() {
xCoordinate++;
setChanged();
notifyObservers(xCoordinate);
}

public int getXCoordinate() {
return xCoordinate;
}

}

public class TestPane extends JPanel implements Observer {

private Model model;

public TestPane(Model model) {
this.model = model;
model.addObserver(this);
}

@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g.setColor(Color.RED);
g.fillRect(model.getXCoordinate(), 1, 50, 50);
g2d.dispose();
}

@Override
public void update(Observable o, Object arg) {
System.out.println(arg);
repaint();
}

}

}

我发现,你永远不会在 Observable 上调用 setChanged,这在我的测试过程中意味着它从未调用 Observer

我还使用 SwingWorker 进行了测试...

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

public static void main(String[] args) {
new Test();
}

public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}

Model model = new Model();

JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane(model));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);

SwingWorker worker = new SwingWorker() {
@Override
protected Object doInBackground() throws Exception {
Thread.sleep(1000);
while (true) {
model.update();
}
}
};
worker.execute();
}

});
}

public class Model extends Observable {

private int xCoordinate;

public synchronized void update() {
xCoordinate++;
setChanged();
notifyObservers(xCoordinate);
}

public synchronized int getXCoordinate() {
return xCoordinate;
}

}

public class TestPane extends JPanel implements Observer {

private Model model;

public TestPane(Model model) {
this.model = model;
model.addObserver(this);
}

@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g.setColor(Color.RED);
g.fillRect(model.getXCoordinate(), 1, 50, 50);
g2d.dispose();
}

@Override
public void update(Observable o, Object arg) {
System.out.println(arg);
repaint();
}

}

}

由于线程同步问题,我同步了对方法的访问,以确保值在更新之间不会更改。因为您使用的是观察者,实际上可以将“新状态”传递给观察者,因此它们在调用时并不依赖于模型的值使用它们。

好吧,总而言之,一旦更新,您需要在 Observable 上调用 setChanged,以便 notifyObservers实际上会调用Observers

正如有人毫无疑问地指出的那样,这种方法存在计时不准确的问题,从本质上来说,Swing TimerThread.sleep 都只能保证“至少”计时并可以在每次更新之间进行验证。

如果您有可变长度操作,这也会影响更新之间的时间。相反,您应该计算执行操作所花费的时间,减去您想要等待的时间,然后使用此延迟来计算您想要在帧之间“等待”多长时间。您还应该使用 System.nanoTime 而不是 System.currentTimeMillis,因为它不会遇到相同的系统时钟同步问题

关于Java Swing 多线程管理与观察者模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42841654/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com