5

この質問は、私がここで尋ねたものと多少関連しています。これで、メイン メソッドとすべてのスイング コンポーネントで構成される "Controller" クラスができました。「高度」という名前の変数で構成される「VTOL」という名前のクラスがあります(現在、この変数を揮発性と宣言しています)。

バックグラウンドで実行されるスレッドで構成されるクラスを次に示します。

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();
            }
        }

    }
}

まず、私が最初に直面していた問題は、プログラムの実行中ずっと「高度」の値を更新できなかったことです。だから私はそれを揮発性と宣言しました(それが良い習慣かどうかはわかりません)

次に、「lblAltitude」という名前の Controller クラスに 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 コンポーネントを変更するときはいつでも、このイベントがイベント ディスパッチ スレッド (つまり EDT) で発生することを確認する必要があります。

于 2012-06-23T17:44:56.760 に答える
2

3 番目のアプローチは、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
}

次に、 EDT で実行されていることがわかっているController の 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 よりも少し緊密に結合されていますが、結合はすべて非常に関連するクラス間であるため、「合理的」であり、ある意味では「より明確」である可能性があります。また、イベント ディスパッチ キューは複数の再描画を結合するため、最初に表示されたほど非効率ではありません。複数のスレッドが内容を更新し、複数の repaint() をキューに入れている場合、最後の repaint() だけが実際に何かを行います。

欠点は、GUI に無数のウィジェットがあり、毎回それらすべてを更新すると、少し遅くなる可能性があることです。しかし、最近のプロセッサは驚くほど高速です。

于 2012-06-23T18:53:12.450 に答える