我正在学习使用GUI实现倒计时器,显示时间减少。我正在使用Groovy的@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 :(得分:2)
是的,它可以。 Nutshell:调用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 }))
}
}
}
答案 1 :(得分: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();
}
});
}
}