5

这个问题与我在这里问的问题有些相关。现在,我有一个“控制器”类,它由主要方法和所有摆动组件组成。有一个名为“VTOL”的类,它由一个名为“altitude”的变量组成(到目前为止,我已将该变量声明为 volatile)。

这是一个由在后台运行的线程组成的类:

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。所以我宣布它为 volatile (我不知道它是否是一个好习惯)

其次,Controller 类中有一个名为“lblAltitude”的 jLabel,我希望在此线程中更改它的值时更新它的值,但不知何故那没有发生。我怎样才能做到这一点?

4

3 回答 3

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);
   }
}
于 2012-06-23T18:12:27.243 回答
6

每当您修改一个 Swing 组件时,您都需要确保该事件发生在 Event Dispatch Thread(即 EDT)中。

于 2012-06-23T17:44:56.760 回答
2

第三种方法是让 Swing 组件了解模型 VTOL。

在 Gravity 中,您将更新 VTOL.altitude,然后在组件上调用 repaint。例如

while (true) {
  VTOL.altitude -= 0.01;
  VTOL.makeAnyOtherChangesHereAsWell();

  controller.repaint();
  // sleep, break etc. left as an exercise for the reader
}

然后,在控制器的 paintComponent() 方法中(或者可能在所有绘图调用中的其他地方,它需要在其他地方的可能性很小......),你知道它在 EDT 上运行

// 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 耦合得更紧一些,但耦合都是在非常相关的类之间,所以它是“合理的”,在某些方面这可能会“更清晰”。并且事件调度队列将结合多次重绘,所以这并不像它第一次出现的那样低效。如果多个线程正在更新内容并将多个 repaints() 排队,则只有最后一个 repaint() 实际执行任何操作。

一个缺点是,如果您的 GUI 有大量小部件,并且您每次都更新它们,这可能会有点慢。但如今处理器速度惊人。

于 2012-06-23T18:53:12.450 回答