2011-12-26 22 views
1

我正在學習使用GUI顯示時間減少來實現倒數計時器。我正在使用Groovy的@Bindable,希望可以在相應的UI標籤中自動顯示時間縮短的更改。@Bindable變量在不同線程中的變化可以反映出相應的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 – 2011-12-26 23:59:51

回答

2

是的,它可以。堅果殼:撥打setTimeStr,而不是直接設置屬性。

繞過setter意味着沒有@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

感謝您的糾正! – 2011-12-27 22:20:07

+0

另一個問題:我在哪裏可以找到更多關於@Bindable相關技術的正式詳細文檔。在我看來,當用谷歌搜索時,很難找到Groovy的全面文檔。它可能被認爲是Java的「糖衣」,而不是一流的語言。對於那些對Java不太瞭解的人來說,這很難,因爲負責精通Java,然後使用Groovy。 – 2011-12-27 22:24:30

+0

@YuShen你在找什麼類型的信息? – 2011-12-27 23:12:51

0

這是我在Stackoverflow上學習的解決方案。我從停止計時器的例子改編而來。關鍵是要使用Swing Timer而不是通用定時器,並使用Listener接口作爲定時器值顯示面板。

我以前使用@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(); 
          } 
          }); 
    } 
} 
相關問題