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

사소한 정리, 노이즈 제거, 속도 디버깅을 단축 지연을 포함, 난에서 발견 "타이머 스윙"자바에 가장 가까운 예를 발견했습니다 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

또 다른 질문 : @ Bindable 관련 기술에 대한 자세한 공식 문서는 어디에서 찾을 수 있습니까? 검색 할 때 Groovy에 대한 철저한 문서를 찾기가 어렵습니다. 그것은 일류 언어가 아닌 자바의 "설탕 코팅"으로 간주 될 수 있습니다. 이것은 Java에 대해 많이 알지 못하는 사람들에게는 어렵습니다. 그루비를 사용하려면 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(); 
          } 
          }); 
    } 
} 
관련 문제