2017-04-08 46 views
1

由於我最後一個問題,我已經使我的計時器程序更復雜,我跑到另一面牆上。 我似乎無法得到兩個定時器的顯示更新。他們打印使用println很好,但如果我使用靜態變量,它會給出正確的值,但只有在計時器中,這是有道理的。所以,無論我做什麼,我似乎無法弄清楚如何獲得活動計時器更新時,它應該和應該更新時間計時器。以下是我的代碼的相關部分。計時器問題 - 更新顯示器

您可以提供有關如何在計時器增量時更新顯示屏的指導嗎?

這個類是計時器的功能:

import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import javax.swing.Timer; 

public class TaskTimer { 

    private int seconds = 0; 
    private int minutes = 0; 
    private int hours = 0; 
    private final int UNIT = 1000; 
    private Timer timer; 
    private String name; 


    public TaskTimer(String name) { 

     ActionListener go = new ActionListener() { 
      @Override 
      public void actionPerformed(ActionEvent e) { 
       System.out.println(count()); 

      } 
     }; 

     timer = new Timer(UNIT, go);  
    } 


    public void begin() { 
     if(ControlTimer.isStopped()) { 
      seconds = 0; 
      minutes = 0; 
      hours = 0; 

     } 
     timer.start(); 
    } 

    public void end() { 
     timer.stop(); 
    } 

    public String count() { 
     if(seconds < 59) { 
      seconds++; 
     } else if(minutes < 59) { 
      seconds = 0; 
      minutes++; 
     } else { 
      seconds = 0; 
      minutes = 0; 
      hours++; 
     } 

     return (String.format("%02d", hours) + ":" 
     + String.format("%02d", minutes) + ":" 
     + String.format("%02d", seconds)); 
    } 

    public int getSeconds() { 
     return seconds; 
    } 

    public void setSeconds(int s) { 
     seconds = s; 
    } 

    public int getMinutes() { 
     return minutes; 
    } 

    public void setMinutes(int m) { 
     minutes = m; 
    } 

    public int getHours() { 
     return hours; 
    } 

    public void setHours(int h) { 
     hours = h; 
    } 

} 

這一個創建面板進入窗口:

import java.awt.Color; 
import java.awt.Component; 
import java.awt.Font; 

import javax.swing.BorderFactory; 
import javax.swing.BoxLayout; 
import javax.swing.JLabel; 
import javax.swing.JPanel; 
import javax.swing.border.EmptyBorder; 
import javax.swing.border.EtchedBorder; 

public class TaskTimerPanel extends JPanel{ 

    String panelName; 
    private static String timerValue; 
    private JLabel timeDisplay; 
    JLabel title; 
    TaskTimer taskTimer; 

    public TaskTimerPanel(String panelName) { 

     // Get timer 
     taskTimer = new TaskTimer(panelName); 

     // Setup Panel 
     JPanel p = new JPanel(); 
     p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); 

     // Add Title Label 
     this.panelName = panelName; 
     title = new JLabel(panelName); 
     p.add(title); 

     // Add Timer Display 
     timerValue = "00:00:00"; 
     timeDisplay = new JLabel(timerValue); 
     p.add(timeDisplay); 

     // Add Formatting 
     title.setAlignmentX(Component.CENTER_ALIGNMENT); 
     timeDisplay.setAlignmentX(CENTER_ALIGNMENT); 
     timeDisplay.setOpaque(true); 
     title.setBorder(new EmptyBorder(10, 10, 10, 10)); 
     timeDisplay.setBorder(BorderFactory.createCompoundBorder(new EtchedBorder(), 
      new EmptyBorder(10, 10, 10, 10))); 
     timeDisplay.setBackground(Color.WHITE); 
     Font font = new Font("Arial", Font.PLAIN, 48); 
     title.setFont(font); 
     timeDisplay.setFont(font); 
     p.setBorder(BorderFactory.createCompoundBorder(new EtchedBorder(), 
      new EmptyBorder(10, 10, 10, 10))); 

     // Add to JFrame 
     add(p); 
    } 
    public JLabel getText() { 
     return timeDisplay; 
    } 

    public void changeDisplay(String time) { 
     getText().setText(time); 
    } 
} 

而且控制器:

import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 

public class ControlTimer { 
    private static boolean stopped = true; 
    private TaskTimerWindow t; 

    public static boolean isStopped() { 
     return stopped; 
    } 

    public void activeTime() { 
     t.activeTimer.taskTimer.begin(); 
     t.breakTimer.taskTimer.end(); 
     stopped = false; 
    } 

    public void breakTime() { 
     if(!stopped) { 
      t.activeTimer.taskTimer.end(); 
      t.breakTimer.taskTimer.begin(); 
     } 
    } 

    public void reset() { 
     if(!stopped) { 
      stopped = true; 
      t.activeTimer.taskTimer.end(); 
      t.breakTimer.taskTimer.end(); 
     } 
    } 


    public ControlTimer() { 
     t = new TaskTimerWindow(); 

     t.buttons.start.addActionListener(new ActionListener() {  
      public void actionPerformed(ActionEvent e) { 
       activeTime(); 
      } 
     }); 

     t.buttons.stop.addActionListener(new ActionListener() { 
      public void actionPerformed(ActionEvent e) { 
       reset(); 
      } 
     }); 

     t.buttons.pause.addActionListener(new ActionListener() { 
      public void actionPerformed(ActionEvent e) { 
        breakTime(); 
      } 
     }); 
    } 

     public static void main(String[] args) { 
      ControlTimer t = new ControlTimer(); 
     } 
    } 

我我忽略了窗口設置和按鈕類,因爲我不認爲它們對於這個問題很重要。

+0

請編輯的問題將其限制爲具有足夠細節的特定問題以確定適當的答案。此外,儘可能限制代碼量。謝謝。 –

+0

嗯,我不確定問題出在哪裏,所以我展示了我認爲可能相關的內容。此外,它非常簡單。定時器不會更新GUI。這應該從我的問題中清楚。 – Kendra

+1

好吧,你已經把它稍微向後一點,而不是將TaskTimerWindow的引用傳遞給Timer,設置一個觀察者模式,允許觀察者註冊感興趣的東西在技術上發生變化時得到通知,這意味着您只需要一個Timer ,因爲它會觸發tick事件。允許觀察者確定當計時器滴答時應該發生什麼 – MadProgrammer

回答

3

讓我們退後一步,爲第二,你似乎需要...

  • A「計時器」,其中有一個固定的持續時間和需要產生某種「嘀」事件,讓觀察員知道它的更新
  • 某種「計時器」,它可以連續運行它們爲每個「定時器」的「名單」的完成

要小心,不傳遞對象的引用到其他什麼實際上沒有責任改變他們或對他們感興趣的對象。相反,您應該考慮使用某種類型的Observer Pattern,這種類型允許您以分離的方式監視對象的更改。

這是"single responsibility principle"

在這種情況下,Timer不關心你的用戶界面,它關心在某個區間運行時,在指定的時間段,發生事件時,「滴答」,當它完成,就是這樣。更新用戶界面或做出任何其他決定不是責任。

當您遇到問題時,您應該花費一些時間將問題分解爲可管理的塊 - AKA自頂向下分解。這使您可以識別的問題,誰是一起涉及哪些信息你可能需要共享

有時間限制的任務方面...基於

什麼我明白你的問題,你有其中有一個定義的持續時間的一些任務,必須提供當

  • 它蜱
  • 完成

的F通知或者,我總是與一些接口開始,這些規定整個合同/我希望暴露給外界(AKA代碼接口,而不是實現)

public interface TimedTask { 
    public void addTimedTaskListener(TimedTaskListener listener); 
    public void removeTimedTaskListener(TimedTaskListener listener); 

    public void addTimedTaskTickListener(TimedTaskTickListener listener); 
    public void removeTimedTaskTickListener(TimedTaskTickListener listener); 

    public void start(); 
    public void stop(); 
    public void reset(); 
    public boolean isRunning(); 

    public Duration getRunDuration(); 
    public Duration getTotalDuration(); 
} 

public class TimedTaskEvent extends EventObject { 

    public enum State { 
     RUNNING, PAUSED, COMPLETED 
    } 

    private State state; 

    public TimedTaskEvent(Object source, State state) { 
     super(source); 
     this.state = state; 
    } 

    public State getState() { 
     return state; 
    } 

} 

public interface TimedTaskListener extends EventListener { 
    public void taskStateChanged(TimedTaskEvent evt); 
} 

public interface TimedTaskTickListener extends EventListener { 
    public void timedTaskTicked(TimedTaskEvent evt); 
} 

好的預期,所以有點事那裏。 TimedTask是一些具有「持續時間」(getRunDuration)和總運行時間(getTotalDuration)或任務運行時間的任務。

它提供了兩個觀察者,一個狀態改變來監視生命週期的變化,一個打勾的通知讓觀察者知道計時器何時更新自己。

它還提供了一些簡單的管理功能,start,stopreset。下面的實現允許作業被「暫停」和「恢復」(通過調用stopstart),但是這只是因爲我的代碼做到這一點:P

通常情況下,我會用abstract實施啓動並實現最常見的功能(在此可能是事件管理),這允許將來的實現將重點放在他們自己實現的重要方面上,但爲了簡潔起見,我已經直接轉到默認實現

public class DefaultTimedTask implements TimedTask { 

    private EventListenerList listenerList = new EventListenerList(); 

    private Timer timer; 

    // How long the timer has been continuosuly running  
    private Duration totalDuration; 
    // The time of the last tick 
    private LocalDateTime tickTime; 

    // How long the timer should run for 
    private Duration runDuration; 

    public DefaultTimedTask(Duration duration, int tickInterval) { 
     runDuration = duration; 
     totalDuration = Duration.ZERO; 
     timer = new Timer(tickInterval, new ActionListener() { 
       @Override 
       public void actionPerformed(ActionEvent e) { 
        if (tickTime == null) { 
         tickTime = LocalDateTime.now(); 
        } 
        LocalDateTime now = LocalDateTime.now(); 
        Duration tickDuration = Duration.between(tickTime, now); 
        totalDuration = totalDuration.plus(tickDuration); 

        fireTaskTimerTicked(); 

        if (totalDuration.compareTo(runDuration) >= 0) { 
         timer.stop(); 
         fireTaskTimerStartedChanged(TimedTaskEvent.State.COMPLETED); 
        } 
       } 
      }); 
    } 

    @Override 
    public void reset() { 
     timer.stop(); 
     tickTime = null; 
     totalDuration = Duration.ZERO; 
    } 

    public void setPaused(boolean paused) { 
     if (paused && timer.isRunning()) { 
      timer.stop(); 
      fireTaskTimerStartedChanged(TimedTaskEvent.State.PAUSED); 
     } else if (!paused && !timer.isRunning()) { 
      tickTime = null; 
      timer.start(); 
      fireTaskTimerStartedChanged(TimedTaskEvent.State.RUNNING); 
     } 
    } 

    protected void fireTaskTimerTicked() { 
     TimedTaskTickListener[] listeners = listenerList.getListeners(TimedTaskTickListener.class); 
     if (listeners.length > 0) { 
      TimedTaskEvent evt = new TimedTaskEvent(this, TimedTaskEvent.State.RUNNING); 
      for (TimedTaskTickListener listener : listeners) { 
       listener.timedTaskTicked(evt); 
      } 
     } 
    } 

    protected void fireTaskTimerStartedChanged(TimedTaskEvent.State state) { 
     TimedTaskListener[] listeners = listenerList.getListeners(TimedTaskListener.class); 
     if (listeners.length > 0) { 
      TimedTaskEvent evt = new TimedTaskEvent(this, state); 
      for (TimedTaskListener listener : listeners) { 
       listener.taskStateChanged(evt); 
      } 
     } 
    } 

    public boolean isPaused() { 
     return !timer.isRunning(); 
    } 

    @Override 
    public void addTimedTaskListener(TimedTaskListener listener) { 
     listenerList.add(TimedTaskListener.class, listener); 
    } 

    @Override 
    public void removeTimedTaskListener(TimedTaskListener listener) { 
     listenerList.remove(TimedTaskListener.class, listener); 
    } 

    @Override 
    public void addTimedTaskTickListener(TimedTaskTickListener listener) { 
     listenerList.add(TimedTaskTickListener.class, listener); 
    } 

    @Override 
    public void removeTimedTaskTickListener(TimedTaskTickListener listener) { 
     listenerList.remove(TimedTaskTickListener.class, listener); 
    } 

    @Override 
    public void start() { 
     setPaused(false); 
    } 

    @Override 
    public void stop() { 
     setPaused(true); 
    } 

    @Override 
    public boolean isRunning() { 
     return timer.isRunning(); 
    } 

    @Override 
    public Duration getTotalDuration() { 
     return totalDuration; 
    } 

    @Override 
    public Duration getRunDuration() { 
     return runDuration; 
    } 

} 

好的,再次,有很多事情要做。此實現依賴於Java 8的新Time API來跟蹤定時器運行的時間。 Timer不是假設Timer是準確的(因爲它不是),而是跟蹤每個滴答之間的時間量,然後將其添加到表示任務「總運行時間」的Duration。這使得實現可以暫停並重新啓動。

一旦定時器運行了至少所需的持續時間,它將生成一個COMPLETED狀態改變並停止。

某種方式來管理它

好吧,這讓我們到一個起點。接下來的問題是,您有許多定時任務需要依次運行,一個接一個地執行。現在你可以簡單地設置鏈接在一起的實現,但是這給定時器增加了額外的責任,相反,我選擇創建一個「管理器」來控制定時器,監控它們的狀態,並且在完成時啓動下一個...

public class TaskList implements TimedTaskListener { 

    private List<TimedTask> timers = new ArrayList<>(25); 
    private boolean running = false; 
    private TimedTask current; 

    public void add(TimedTask timer) { 
     timer.addTimedTaskListener(this); 
     timers.add(timer); 
    } 

    public void remove(TimedTask timer) { 
     timer.removeTimedTaskListener(this); 
     timers.remove(timer); 
    } 

    public boolean isRunning() { 
     return running; 
    } 

    public void start() { 
     if (current != null) { 
      current.start(); 
      running = true; 
     } else { 
      startNext(); 
     } 
    } 

    public void stop() { 
     if (current != null) { 
      current.stop(); 
     } 
     running = false; 
    } 

    protected void startNext() { 
     if (!timers.isEmpty()) { 
      current = timers.remove(0); 
      current.start(); 
      running = true; 
     } else { 
      running = false; 
     } 
    } 

    @Override 
    public void taskStateChanged(TimedTaskEvent evt) { 
     switch (evt.getState()) { 
      case COMPLETED: 
       TimedTask timer = (TimedTask) evt.getSource(); 
       remove(timer); 
       startNext(); 
       break; 
     } 
    } 
} 

真的,沒有什麼特別的

將其組合在一起...

嗯,這是一切優秀和良好的,但你會如何使用呢?

基本上,你將創建一個TaskList,你會因爲你需要的TimedTask儘可能多的情況下,確保註冊爲TimedTaskListener得到打勾更新,因此您可以更新UI(或者你需要做什麼呢以往),將它們添加到TaskList,當你準備好了,啓動它,例如...

import java.awt.EventQueue; 
import java.awt.GridBagLayout; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.time.Duration; 
import java.time.LocalDateTime; 
import java.util.ArrayList; 
import java.util.EventListener; 
import java.util.EventObject; 
import java.util.List; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.JPanel; 
import javax.swing.Timer; 
import javax.swing.UIManager; 
import javax.swing.UnsupportedLookAndFeelException; 
import javax.swing.event.EventListenerList; 

public class TimerTest { 

    public static void main(String[] args) { 
     new TimerTest(); 
    } 

    public TimerTest() { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
       } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { 
        ex.printStackTrace(); 
       } 

       JFrame frame = new JFrame("Testing"); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.add(new TestPane()); 
       frame.pack(); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 
      } 
     }); 
    } 

    public class TestPane extends JPanel { 

     private JLabel label = new JLabel("00:00:00.00"); 

     public TestPane() { 
      setLayout(new GridBagLayout()); 
      add(label); 
      DefaultTimedTask task = new DefaultTimedTask(Duration.ofMinutes(3), 100); 
      task.addTimedTaskTickListener(new TimedTaskTickListener() { 
       @Override 
       public void timedTaskTicked(TimedTaskEvent evt) { 
        TimedTask task = (TimedTask) evt.getSource(); 
        Duration duration = task.getTotalDuration(); 
        label.setText(format(duration)); 
       } 
      }); 

      TaskList taskList = new TaskList(); 
      taskList.add(task); 
      taskList.start(); 
     } 

     public String format(Duration duration) { 
      long hours = duration.toHours(); 
      duration = duration.minusHours(hours); 
      long minutes = duration.toMinutes(); 
      duration = duration.minusMinutes(minutes); 
      long millis = duration.toMillis(); 
      double seconds = millis/1000.0; 

      return String.format("%02d:%02d:%02.02f", hours, minutes, seconds); 
     } 
    } 
} 

其他的事情...

這是想法很簡單概述,你會大概也想提供一些觀察者來TaskList它可以創建在所有任務完成時提供通知。

您還需要某種標識符添加到TimedTaskinterface,就像一個name或別的東西,你可以用,只是讓你知道什麼是實際發生和定時器實際運行

2

我沒有回答過去的問題作爲公正的警告。我試圖想出一個很好的解決方案,但措辭可能不是很好。我希望這可以抱你了,直到一個更好的合格堆回覆這裏所說:


我能想到的最好的辦法是從Java使用組件庫。我假設這個顯示計時器不是JDK的一部分,而是你從頭開始創建的東西。由於我沒有花時間來運行你的代碼,因爲我假設在沒有你省略的文件的情況下嘗試這樣做是毫無意義的,這可能是一個體面的解決方案。使用Component對象處理要打印出來的所有需要​​實現的字符串是JFrame類中的另一個計時器,用於處理組件調用repaint方法的頻率。 祝你好運,我希望這至少有助於推動。歡呼聲

//Class Variable 
private static YourTimerComponent timerComponent = new YourTimerComponent(); 

//Put everything below where you made the JFrame 
static int delay = 1000; //Milliseconds 

frame.add(timerComponent); 

ActionListener refresh = new ActionListener() { 
    public void actionPerformed(ActionEvent event) { 
     timerComponent.repaint(); 
    } 
}; 
new Timer(delay, refresh).start(); 

然後您需要創建繪製字符串的組件。您還應該決定在JFrame類中的哪個位置顯示您的JFrame。

+0

*「我假設這個計時器不是JDK的一部分」* - 'javax.swing.Timer' – MadProgrammer

+0

對不起,我會重新提示。我的意思是一個可在JFrame中顯示的計時器不在JDK中(這就是你想要編寫的代碼..我想?)。如果你可以讓它們以system.out.println()的方式工作,那麼你可以將它重寫爲一個JComponent中的drawString,並根據附加的定時器進行更新。這可能不是最健全的解決方案,但它是可能的。 –