这个问题与我提出的问题HERE有些关系。 现在,我有一个班级"控制器"它由主要方法和所有摆动组件组成。有一个名为" VTOL"它由一个名为" altitude"的变量组成(我已经宣布这个变量现在是易变的。)
这是一个由在后台运行的线程组成的类:
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author Vineet
*/
public class Gravity extends Thread {
String altStr;
double alt;
Controller ctrl = new Controller();
@Override
public void run() {
while (true) {
alt=VTOL.altitude;
System.out.println(alt);
alt = alt-0.01;
VTOL.altitude= (int) alt;
altStr=new Integer(VTOL.altitude).toString();
ctrl.lblAltitude.setText(altStr);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
首先,我最初面临的问题是我无法更新"海拔高度"在整个计划执行期间,它仍为0。所以我宣称它是不稳定的(我不知道它是不是一个好习惯)
其次,在Controller类中有一个名为" lblAltitude"的jLabel,我希望将其值更新为此线程中的更改,但不知何故没有发生。 我怎么能这样做?
答案 0 :(得分:7)
解决方案是使用SwingPropertyChangeSupport对象,使高度成为此支持对象的“绑定”属性,以使您的GUI监听器到此模型类,从而通知GUI高度变化。
如,
import java.beans.PropertyChangeListener;
import javax.swing.event.SwingPropertyChangeSupport;
public class Gravity implements Runnable {
public static final String ALTITUDE = "altitude";
private SwingPropertyChangeSupport swingPcSupport = new SwingPropertyChangeSupport(this);
private volatile double altitude;
@Override
public void run() {
while (true) {
double temp = altitude + 10;
setAltitude(temp); // fires the listeners
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public double getAltitude() {
return altitude;
}
public void setAltitude(double altitude) {
Double oldValue = this.altitude;
Double newValue = altitude;
this.altitude = newValue;
// this will be fired on the EDT since it is a SwingPropertyChangeSupport object
swingPcSupport.firePropertyChange(ALTITUDE, oldValue, newValue);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
swingPcSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
swingPcSupport.removePropertyChangeListener(listener);
}
}
有关更完整的可运行示例:
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
import javax.swing.event.SwingPropertyChangeSupport;
public class GravityTestGui extends JPanel {
private static final long ALT_SLEEP_TIME = 400;
private static final double ALT_DELTA = 5;
JLabel altitudeLabel = new JLabel(" ");
private Gravity gravity = new Gravity(ALT_SLEEP_TIME, ALT_DELTA);
public GravityTestGui() {
add(new JLabel("Altitude:"));
add(altitudeLabel);
gravity.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent pcEvt) {
if (Gravity.ALTITUDE.equals(pcEvt.getPropertyName())) {
String altText = String.valueOf(gravity.getAltitude());
altitudeLabel.setText(altText);
}
}
});
new Thread(gravity).start();
}
private static void createAndShowGui() {
GravityTestGui mainPanel = new GravityTestGui();
JFrame frame = new JFrame("GravityTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class Gravity implements Runnable {
public static final String ALTITUDE = "altitude";
private SwingPropertyChangeSupport swingPcSupport = new SwingPropertyChangeSupport(this);
private volatile double altitude;
private long sleepTime;
private double delta;
public Gravity(long sleepTime, double delta) {
this.sleepTime = sleepTime;
this.delta = delta;
}
@Override
public void run() {
while (true) {
double temp = altitude + delta;
setAltitude(temp); // fires the listeners
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public double getAltitude() {
return altitude;
}
public void setAltitude(double altitude) {
Double oldValue = this.altitude;
Double newValue = altitude;
this.altitude = newValue;
// this will be fired on the EDT since it is a SwingPropertyChangeSupport object
swingPcSupport.firePropertyChange(ALTITUDE, oldValue, newValue);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
swingPcSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
swingPcSupport.removePropertyChangeListener(listener);
}
}
答案 1 :(得分:6)
每当修改Swing组件时,都需要确保在Event Dispatch Thread(即EDT)中发生此事件。
答案 2 :(得分:2)
第三种方法是让您的Swing组件了解模型VTOL。
在Gravity中,您将更新VTOL.altitude,然后在组件上调用repaint。 e.g。
while (true) {
VTOL.altitude -= 0.01;
VTOL.makeAnyOtherChangesHereAsWell();
controller.repaint();
// sleep, break etc. left as an exercise for the reader
}
然后,在paintComponent()方法中(或者可能是所有绘制调用中的其他地方,它可能需要在其他地方......),你知道正在运行在美国东部时间
// update my widgets from the VTOL model - may want this in a method
String altStr=new Integer(VTOL.altitude).toString();
this.lblAltitude.setText(altStr);
// may be more, e.g. ...
this.lblFuelSupply.setText(VTOL.getFuelSupply());
super.paintComponent(); // now go draw stuff...
这比SwingPropertyChangeSupport更加紧密耦合,但耦合是在非常相关的类之间进行的,因此它是“合理的”,并且在某些方面可能更“清晰”。并且事件调度队列将结合多个重绘,因此这不像它首次出现那样低效。如果多个线程正在更新内容并排队多个重绘(),则只有最后一个重绘()实际上做了任何事情。
缺点是如果你的GUI有很多小部件,并且你每次都会更新所有这些小部件,这可能会有点慢。但是现在处理器的速度非常快。