1

時間の短縮を示す GUI を使用してカウント ダウン タイマーを実装する方法を学習しています。@Bindable時間短縮の変更が対応する UI ラベルに自動的に表示されることを期待して、Groovy を使用しています。

カウントダウン時間の値の削減は、UI スレッドとは別のタイマー スレッドで行われます。ただし、カウントダウン タイマーは UI で更新されません。

UI のカウントダウン時間を適切に更新する適切な方法は何ですか?

import groovy.swing.SwingBuilder
import java.awt.FlowLayout as FL
import javax.swing.BoxLayout as BXL
import javax.swing.JFrame
import groovy.beans.Bindable
import java.util.timer.*  

// A count-down timer using Bindable to reflcet the reduction of time, when the reduction is done in a TimerTask thread

class CountDown {
  int delay = 5000   // delay for 5 sec.  
  int period = 60*1000  // repeat every minute.  
  int remainingTime = 25*60*1000
  // hope to be able to update the display of its change:
  @Bindable String timeStr = "25:00"
  public void timeString () {
    int seconds = ((int) (remainingTime / 1000))  % 60 ;
    int minutes =((int) (remainingTime / (1000*60))) % 60;
    timeStr = ((minutes < 9) ? "0" : "") + String.valueOf (minutes)  + ":" + ((seconds < 9) ? "0" : "") + String.valueOf (seconds)
  }
  public void update () {
    if (remainingTime >= period)
      remainingTime =  (remainingTime - period)
    // else // indicate the timer expires on the panel
    // println remainingTime
    // convert remainingTime to be minutes and secondes
    timeString()
    println timeStr // this shows that the TimerTaskCountDown thread is producting the right reduction to timeStr
  }
}

model = new CountDown()
class TimerTaskCountDown extends TimerTask {
  public TimerTaskCountDown (CountDown modelIn) {
    super()
    model = modelIn
  }
  CountDown model
  public void run() {
    model.update() // here change to model.timeStr does not reflected
  }  
}  

Timer timer = new Timer()  
timer.scheduleAtFixedRate(new TimerTaskCountDown(model), model.delay, model.period)

def s = new SwingBuilder()
s.setVariable('myDialog-properties',[:])
def vars = s.variables
def dial = s.dialog(title:'Pomodoro', id:'working', modal:true, 
                    // locationRelativeTo:ui.frame, owner:ui.frame, // to be embedded into Freeplane eventually
                    defaultCloseOperation:JFrame.DISPOSE_ON_CLOSE, pack:true, show:true) {
  panel() {
    boxLayout(axis:BXL.Y_AXIS)
    panel(alignmentX:0f) {
      flowLayout(alignment:FL.LEFT)
      label text: bind{"Pomodoro time: " + model.timeStr}
    }
    panel(alignmentX:0f) {
      flowLayout(alignment:FL.RIGHT)
      button(action: action(name: 'STOP', defaultButton: true, mnemonic: 'S',
                            closure: {model.timeStr = "stopped"; vars.ok = true//; dispose() // here the change to model.timeStr gets reflected in the label
                            }))
    }
  }
}
4

2 に答える 2

2

はい、できます。setTimeStr一言で言えば、プロパティを直接設定する代わりに呼び出します。

セッターをバイパスするということは、によって追加されたコード@Bindableが実行されていなかったことを意味していたため、プロパティ変更通知は送信されていませんでした。

その他の編集には、マイナーなクリーンアップ、ノイズの除去、デバッグを高速化するための遅延の短縮などが含まれます。

import groovy.swing.SwingBuilder
import java.awt.FlowLayout as FL
import javax.swing.BoxLayout as BXL
import javax.swing.JFrame
import groovy.beans.Bindable
import java.util.timer.*  

class CountDown {
  int delay = 1000
  int period = 5 * 1000
  int remainingTime = 25 * 60 *1000

  @Bindable String timeStr = "25:00"

  public void timeString() {
    int seconds = ((int) (remainingTime / 1000))  % 60 ;
    int minutes =((int) (remainingTime / (1000*60))) % 60;

    // Here's the issue
    // timeStr = ((minutes < 9) ? "0" : "") + minutes + ":" + ((seconds < 9) ? "0" : "") + seconds
    setTimeStr(String.format("%02d:%02d", minutes, seconds))
  }

  public void update() {
    if (remainingTime >= period) {
      remainingTime -= period
    }

    timeString()
  }
}

class TimerTaskCountDown extends TimerTask {
  CountDown model

  public TimerTaskCountDown (CountDown model) {
    super()
    this.model = model
  }

  public void run() {
    model.update()
  }  
}  

model = new CountDown()
ttcd = new TimerTaskCountDown(model)

timer = new Timer()  
timer.scheduleAtFixedRate(ttcd, model.delay, model.period)

def s = new SwingBuilder()
s.setVariable('myDialog-properties',[:])

def dial = s.dialog(title:'Pomodoro', id:'working', modal:false,  defaultCloseOperation:JFrame.DISPOSE_ON_CLOSE, pack:true, show:true) {
  panel() {
    boxLayout(axis:BXL.Y_AXIS)
    panel(alignmentX:0f) {
      flowLayout(alignment:FL.LEFT)
      label text: bind { "Pomodoro time: " + model.timeStr }
    }

    panel(alignmentX:0f) {
      flowLayout(alignment:FL.RIGHT)
      button(action: action(name: 'STOP', defaultButton: true, mnemonic: 'S', closure: { model.timeStr = "stopped"; vars.ok = true }))
    }
  }
}
于 2011-12-27T14:18:24.897 に答える
0

以下はStackoverflowで調べて見つけた解決策です。ストップタイマーの例から適応しました。重要なのは、汎用タイマーの代わりに Swing Timer を使用し、タイマー値表示パネルに Listener インターフェイスを使用することです。

@Bindable を使用する以前の試みは引き続き機能しますが、setTimeStr ルーチンを介してバインド可能な timeStr にすべての設定を行う必要があります。(デイブの助けに感謝します!)

Stackoverflow は学ぶのに最適な場所です。

これがコードです。

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;

/** @following the example of http://stackoverflow.com/questions/2576909 */
/** adapted for count-down timer */
public class JTimeLabel extends JLabel implements ActionListener {

  private static final String Start = "Start";
  private static final String Stop = "Stop";
  private DecimalFormat df = new DecimalFormat("000.0");
  private Timer timer = new javax.swing.Timer(100, this);

  private int countDownMinutes = 25;
  private long countDownMillis = 25*60*1000;
  private long expireMillis = countDownMillis + System.currentTimeMillis();

  public JTimeLabel() {
    this.setHorizontalAlignment(JLabel.CENTER);
    this.setText(when());
  }

  public void actionPerformed(ActionEvent ae) {// this is for update the timer value
    setText(when());
  }

  public void start() { // reset the expiration time and start the timer
    expireMillis = countDownMillis + System.currentTimeMillis();
    timer.start();
  }

  public void stop() {
    timer.stop();
  }

  private String when() {// show count-down timer value
    if (expireMillis > System.currentTimeMillis()) {
      long remainingMillis = expireMillis - System.currentTimeMillis()
      int seconds = ((int) (remainingMillis / 1000))  % 60 ;
      int minutes =((int) (remainingMillis / (1000*60))) % 60;
      return (String.format("%02d:%02d", minutes, seconds))
    } else {// handle the completion of the count-down timer
      timer.stop ();
      return "00:00"
    }   
  } 

  private static void create() {
    JFrame f = new JFrame();
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    final JTimeLabel jtl = new JTimeLabel();
    jtl.setFont(new Font("Dialog", Font.BOLD, 32));
    f.add(jtl, BorderLayout.CENTER);

    final JButton button = new JButton(Stop);
    button.addActionListener(new ActionListener() {
                               public void actionPerformed(ActionEvent e) {
                                 String cmd = e.getActionCommand();
                                 if (Stop.equals(cmd)) {
                                   jtl.stop();
                                   button.setText(Start);
                                 } else {
                                   jtl.start();
                                   button.setText(Stop);
                                 }

                               }
                             });
    f.add(button, BorderLayout.SOUTH);
    f.pack();
    f.setVisible(true);
    jtl.start();
  }

  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
                             public void run() {
                               create();
                             }
                           });
  }
}
于 2011-12-27T13:55:18.943 に答える