2011-12-26 14 views
1

私は時間の削減を示すGUIでカウントダウンタイマーを実装する方法を学んでいます。私はGroovyの@Bindableを使用して、対応するUIラベルに時間の変更が自動的に表示されることを期待しています。異なるスレッドの@Bindable変数の変更を対応するUI要素に反映できますか?

カウントダウン時間値の減少は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 
          })) 
    } 
    } 
} 
+0

が含まれ、私はで発見「スイングタイマー」を使用してJavaで最も近い例を見つけましたhttp://stackoverflow.com/questions/2576353/stop-a-stopwatch/2576909#2576909 –

答えて

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 })) 
    } 
    } 
} 
+0

訂正していただきありがとうございます! –

+0

もう1つの質問:@Bindable関連技術について、より正式な詳細な文書はどこにありますか?グービックのための完全なドキュメントを見つけるのは難しいです。これは、Javaの "糖衣"とみなされ、ファーストクラスの言語ではありません。これはJavaについてあまり知らない人にとっては難しいことですが、Groovyを使用するにはJavaに堪能でなければなりません。 –

+0

@YuShenどのような情報をお探しですか? –

0

ここで私がStackoverflowで勉強して見つけた解決策です。私はストップタイマーの例から順応しました。キーは、汎用タイマーの代わりにスイングタイマーを使用し、タイマー値表示パネルのリスナーインターフェースを使用することです。 @Bindableを使用する

私の以前の試みは、まだ動作しますが、それはすべてのsetTimeStrのルーチンをバインド可能timeStrに設定する必要があります。 (Daveの助けに感謝!)

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(); 
          } 
          }); 
    } 
} 
関連する問題