Может ли изменение переменной @Bindable в другом потоке отражаться на соответствующем элементе пользовательского интерфейса? - PullRequest
1 голос
/ 26 декабря 2011

Я учусь реализовывать таймер обратного отсчета с графическим интерфейсом, показывающим сокращение времени.Я использую Groovy @Bindable в надежде, что изменение сокращения времени может автоматически отображаться в соответствующей метке пользовательского интерфейса.

Уменьшение значения времени обратного отсчета выполняется в потоке таймера, отделенном от потока пользовательского интерфейса.Однако таймер обратного отсчета не обновляется в пользовательском интерфейсе.

Как правильно правильно установить время обратного отсчета в обновлении интерфейса?

Ответы [ 2 ]

2 голосов
/ 27 декабря 2011

Да, это возможно. В двух словах: звоните 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 голосов
/ 27 декабря 2011

Вот решение, которое я нашел, изучая Stackoverflow. Я адаптировался на примере таймера остановки. Ключом является использование Swing Timer вместо обычного таймера и интерфейса Listener для панели отображения значений таймера.

Моя предыдущая попытка использовать @Bindable все еще работала бы, но для нее требовались все установки привязываемого timeStr с помощью процедуры setTimeStr. (Благодаря помощи Дейва!)

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