2017-04-08 64 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,并根据附加的定时器进行更新。这可能不是最健全的解决方案,但它是可能的。 –