当前位置: 首页 > 知识库问答 >
问题:

问题:创建一个非常精确的摇摆计时器

辛建业
2023-03-14

为了使SwingTimer准确,我喜欢@Tony Docherty On CR建议的逻辑和示例。这是链接。

为了一次又一次地突出显示给定的单词,总是会有几微秒的延迟。如果我有要突出显示的单词,比如说:“你好,你好”,每个单词的值分别是(延迟):200300400毫秒,那么计时器实际花费的时间总是更多。比如说,如果我有很多单词的话,需要216毫秒,而不是200毫秒。。最后,额外的延迟是显而易见的。

我必须突出显示每个字母,说:“h”e“l”l“0”每个字母应该得到200/长度(即5)=40毫秒左右。设置每个字母后的延迟。

我的逻辑是,在开始这个过程之前,用当前时间,比如说startTime。另外,计算totalDelay,即totalDelay=delay/。长度()。

现在检查条件:(startTime totalDelay System.currentTime)如果这是-ve,这意味着时间消耗更多,所以跳过字母。检查直到出现正延迟。这意味着我将添加到现在的计时,并用开始时流程所用时间的差异进行过度检查。

这可能导致跳过以突出显示字母。

但有点不对劲。什么,我很难理解。可能是循环的问题。我见过它两次进入循环(检查时间是否为-ve)。但事实并非如此。我也不确定是否会安排下一次延期。有什么想法吗?

这里是一个SSCCE:

    import java.awt.Color;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.lang.reflect.InvocationTargetException;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JTextPane;
    import javax.swing.SwingUtilities;
    import javax.swing.Timer;
    import javax.swing.text.BadLocationException;
    import javax.swing.text.DefaultStyledDocument;
    import javax.swing.text.StyleConstants;
    import javax.swing.text.StyledDocument;

    public class Reminder {
        private static final String TEXT = "arey chod chaad ke apnee saleem ki gali anarkali disco chalo";
        private static final String[] WORDS = TEXT.split(" ");
        private JFrame frame;
        private Timer timer;
        private StyledDocument doc;
        private JTextPane textpane;
        private int[] times = new int[100];
      private long totalDelay=0,startTime=0;

        private int stringIndex = 0;
        private int index = 0;

        public void startColoring() {
              times[0]=100;times[9]=200;times[10]=200;times[11]=200;times[12]=200;
              times[1]=400;times[2]=300;times[3]=900;times[4]=1000;times[5]=600;times[6]=200;times[7]=700;times[8]=700;

      ActionListener actionListener = new ActionListener() {
       @Override
       public void actionPerformed(ActionEvent actionEvent) 
       {

       doc.setCharacterAttributes(stringIndex, 1, textpane.getStyle("Red"), true);
        stringIndex++;

 try {

 if (stringIndex >= doc.getLength() || doc.getText(stringIndex, 1).equals(" ")|| doc.getText(stringIndex, 1).equals("\n"))
 {
                            index++;
  }
    if (index < WORDS.length) {

       double delay = times[index];
     totalDelay+=delay/WORDS[index].length();

  /*Check if there is no -ve delay, and you are running according to the time*/
  /*The problem is here I think. It's just entered this twice*/
   while(totalDelay+startTime-System.currentTimeMillis()<0)
      { 
      totalDelay+=delay/WORDS[index].length();
      stringIndex++;
     /*this may result into the end of current word, jump to next word.*/
    if (stringIndex >= doc.getLength() || doc.getText(stringIndex, 1).equals(" ") || doc.getText(stringIndex, 1).equals("\n"))
       {
   index += 1;
   totalDelay+=delay/WORDS[index].length();
       }
      }

     timer.setDelay((int)(totalDelay+startTime-System.currentTimeMillis()));

                        } 
else {
         timer.stop();
    System.err.println("Timer stopped");
       }
                    } catch (BadLocationException e) {
                        e.printStackTrace();
                    }
                }
            };

            startTime=System.currentTimeMillis();
            timer = new Timer(times[index], actionListener);
            timer.setInitialDelay(0);
            timer.start();
        }

        public void initUI() {
            frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            JPanel panel = new JPanel();
            doc = new DefaultStyledDocument();
            textpane = new JTextPane(doc);
            textpane.setText(TEXT);
            javax.swing.text.Style style = textpane.addStyle("Red", null);
            StyleConstants.setForeground(style, Color.RED);
            panel.add(textpane);
            frame.add(panel);
            frame.pack();
            frame.setVisible(true);
        }

        public static void main(String args[]) throws InterruptedException, InvocationTargetException {
            SwingUtilities.invokeAndWait(new Runnable() {
                @Override
                public void run() {
                    Reminder reminder = new Reminder();
                    reminder.initUI();
                    reminder.startColoring();
                }
            });
        }
    }

更新:

为了更好地理解:

@Tony Docherty给出的示例:

让我们以“测试”这个词为例,说它需要高亮1秒钟,因此每个字母高亮250ms。按照你最初的方式做意味着你为每个字母设置了250ms的计时器,但是如果每个周期实际上花费了260ms,让我们说“e”周期花费了400ms(可能是由于气相色谱或其他使用CPU周期的原因),到单词结束时,你将花费比你应该花费的多180毫秒。这个错误将继续为每个单词建立,直到错误如此之大,高亮在视觉上不再同步。

我尝试的方法是,不要反复说这个字母需要突出显示x个时间量,而是计算每个字母相对于序列开始的时间,即T=250,e=500,s=750,T=1000。

所以要得到实际的时间延迟,你需要加上开始时间,减去当前时间。使用我上面给出的计时来运行该示例:

StartTime   Letter   Offset    CurrentTime    Delay  ActualTimeTaken   
100000         T       250       100010        240      250  
100000         e       500       100260        240      400  
100000         s       750       100660         90      100  
100000         t      1000       100760        240      250  

因此,您现在应该能够看到,每封信的时间都进行了调整,以考虑到前一封信的任何时间超限。当然,可能是时间超限太大了,以至于你不得不跳过突出显示下一个字母(或者可能超过1个),但至少我会保持大致同步。

编辑SSCCE

更新2

在第一阶段,我会记下每个单词的时间。也就是说,当用户点击ESC键时,会存储特定单词的时间(他在后台播放歌曲时会这样做)当按下ESC键时,当前单词将高亮显示,并且当前单词上花费的时间将存储在一个数组中。我一直在储存时间。当用户结束时,现在我想根据设定的时间突出显示单词。所以在这里,用户的时间安排很重要。如果计时很快,那么单词的突出显示也很快;如果计时很慢,反之亦然。

最新进展

下面的答案有不同的逻辑,但令我惊讶的是,它们的工作原理基本相同。我在所有逻辑(包括我的)中发现了一个非常奇怪的问题,它们似乎在少数几行中都能完美工作,但在那之后,它们获得了速度,这也不是很慢,而是有着巨大的差异。

此外,如果你认为我应该以不同的方式思考,你的建议将受到高度赞赏。

共有3个答案

彭鸿文
2023-03-14

你考虑过java吗。util。计时器和时间表固定日期?你需要做一些额外的工作来完成EDT上的事情,但它应该可以解决累积延迟的问题。

白祺然
2023-03-14

好的,我一直在看一些代码(我在你上一个关于卡拉OK定时器的问题中发布的代码)

使用该代码,我建立了一些测量系统,使用System.nanoTime()通过System.out.println(),这将帮助我们看到发生了什么:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;

public class KaraokeTest {

    private int[] timingsArray = {1000, 1000, 9000, 1000, 1000, 1000, 1000, 1000, 1000, 1000};//word/letters timings
    private String[] individualWordsToHighlight = {" \nHello\n", " world\n", " Hello", " world", " Hello", " world", " Hello", " world", " Hello", " world"};//each individual word/letters to highlight
    private int count = 0;
    private final JTextPane jtp = new JTextPane();
    private final JButton startButton = new JButton("Start");
    private final JFrame frame = new JFrame();
    //create Arrays of individual letters and their timings
    final ArrayList<String> chars = new ArrayList<>();
    final ArrayList<Long> charsTiming = new ArrayList<>();

    public KaraokeTest() {
        initComponents();
    }

    private void initComponents() {
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);

        for (String s : individualWordsToHighlight) {
            String tmp = jtp.getText();
            jtp.setText(tmp + s);
        }
        jtp.setEditable(false);

        startButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                startButton.setEnabled(false);
                count = 0;
                charsTiming.clear();
                chars.clear();

                for (String s : individualWordsToHighlight) {
                    for (int i = 0; i < s.length(); i++) {
                        chars.add(String.valueOf(s.charAt(i)));
                        //System.out.println(String.valueOf(s.charAt(i)));
                    }
                }

                //calculate each letters timings
                for (int x = 0; x < timingsArray.length; x++) {
                    for (int i = 0; i < individualWordsToHighlight[x].length(); i++) {
                        individualWordsToHighlight[x] = individualWordsToHighlight[x].replace("\n", " ").replace("\r", " ");//replace line breaks
                        charsTiming.add((long) (timingsArray[x] / individualWordsToHighlight[x].trim().length()));//dont count spaces
                        //System.out.println(timingsArray[x] / individualWordsToHighlight[x].length());
                    }
                }

                Timer t = new Timer(1, new AbstractAction() {
                    long startTime = 0;
                    long acum = 0;
                    long timeItTookTotal = 0;
                    long dif = 0, timeItTook = 0, timeToTake = 0;
                    int delay = 0;

                    @Override
                    public void actionPerformed(ActionEvent ae) {
                        if (count < charsTiming.size()) {

                            if (count == 0) {
                                startTime = System.nanoTime();
                                System.out.println("Started: " + startTime);
                            }

                            timeToTake = charsTiming.get(count);
                            acum += timeToTake;

                            //highlight the next word
                            highlightNextWord();

                            //System.out.println("Acum " + acum);
                            timeItTook = (acum - ((System.nanoTime() - startTime) / 1000000));
                            timeItTookTotal += timeItTook;
                            //System.out.println("Elapsed since start: " + (System.nanoTime() - startTime));
                            System.out.println("Time the char should take: " + timeToTake);
                            System.out.println("Time it took: " + timeItTook);
                            dif = (timeToTake - timeItTook);
                            System.out.println("Difference: " + dif);
                            //System.out.println("Difference2 " + (timeToTake - dif));

                            //calculate start of next letter to highlight less the difference it took between time it took and time it should actually take
                            delay = (int) (timeToTake - dif);

                            if (delay < 1) {
                                delay = 1;
                            }

                            //restart timer with new timings
                            ((Timer) ae.getSource()).setInitialDelay((int) timeToTake);//timer is usually faster thus the entire highlighting will be done too fast
                            //((Timer) ae.getSource()).setInitialDelay(delay);
                            ((Timer) ae.getSource()).restart();

                        } else {//we are at the end of the array
                            long timeStopped = System.nanoTime();
                            System.out.println("Stopped: " + timeStopped);
                            System.out.println("Time it should take in total: " + acum);
                            System.out.println("Time it took using accumulator of time taken for each letter: " + timeItTookTotal
                                    + "\nDifference: " + (acum - timeItTookTotal));
                            long timeItTookUsingNanoTime = ((timeStopped - startTime) / 1000000);
                            System.out.println("Time it took using difference (endTime-startTime): " + timeItTookUsingNanoTime
                                    + "\nDifference: " + (acum - timeItTookUsingNanoTime));
                            reset();
                            ((Timer) ae.getSource()).stop();//stop the timer
                        }
                        count++;//increment counter
                    }
                });
                t.setRepeats(false);
                t.start();
            }
        });

        frame.add(jtp, BorderLayout.CENTER);
        frame.add(startButton, BorderLayout.SOUTH);

        frame.pack();
        frame.setVisible(true);
    }

    private void reset() {
        startButton.setEnabled(true);
        jtp.setText("");
        for (String s : individualWordsToHighlight) {
            String tmp = jtp.getText();
            jtp.setText(tmp + s);
        }
        JOptionPane.showMessageDialog(frame, "Done");
    }

    private void highlightNextWord() {
        //we still have words to highlight
        int sp = 0;
        for (int i = 0; i < count + 1; i++) {//get count for number of letters in words (we add 1 because counter is only incrementd after this method is called)
            sp += 1;
        }

        while (chars.get(sp - 1).equals(" ")) {
            sp += 1;
            count++;
        }

        //highlight words
        Style style = jtp.addStyle("RED", null);
        StyleConstants.setForeground(style, Color.RED);
        ((StyledDocument) jtp.getDocument()).setCharacterAttributes(0, sp, style, true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new KaraokeTest();
            }
        });
    }
}

我的电脑上的输出是:

开始:10289712615974

该字符所需时间:166

时间:165

差异1

...

该字符所需时间:166

时间:155

差异11

...

该字符所需时间:166

时间:5分钟

差异161

停止:10299835063084

总共需要的时间: 9960

使用每个字母所用时间的累加器所用时间:5542

差异: 4418

使用差异所需的时间(endTime startTime):10122

差异:-162

因此,我的结论是,Swing定时器的运行速度实际上比我们预期的要快,因为定时器sactionPerformed中的代码不一定需要与字母预期高亮显示时间一样长的时间。这当然会导致雪崩效应,即定时器运行得越快/越慢,差异就越大/越小,定时器下次执行重启(…) 将在不同的时间进行,即更快或更慢。

在代码中执行以下操作:

//calculate start of next letter to highlight less the difference it took between time it took and time it should actually take
delay = (int) (timeToTake - dif);


//restart timer with new timings
//((Timer) ae.getSource()).setInitialDelay((int)timeToTake);//timer is usually faster thus the entire highlighting will be done too fast
((Timer) ae.getSource()).setInitialDelay(delay);
((Timer) ae.getSource()).restart();

产生更准确的结果(每封信的最大延迟快4毫秒):

开始:10813491256556

该字符所需时间:166

花的时间: 164

差异2

...

该字符所需时间:166

花的时间: 164

差异2

...

该字符所需时间:166

时间:162

差异4

停止:10823452105363

总共需要的时间: 9960

使用每个字母所用时间的累加器所用时间:9806

差异:154

使用差异所需的时间(endTime startTime):9960

差异: 0

秦鸿羽
2023-03-14

我认为,要做到这一点,你需要一个摆动计时器,它以恒定的速率滴答作响,比如15毫秒,只要它足够快,以允许你所需的时间粒度,然后当经过的时间是你所需的时间时,在计时器内触发所需的行为。

  • 换句话说,根本不改变计时器的延迟,只需根据需要更改所需的时间

我建议的代码应该是这样的:

     public void actionPerformed(ActionEvent actionEvent) {

        if (index > WORDS.length || stringIndex >= doc.getLength()) {
           ((Timer)actionEvent.getSource()).stop();
        }

        currentElapsedTime = calcCurrentElapsedTime();
        if (currentElapsedTime >= elapsedTimeForNextChar) {
           setNextCharAttrib(stringIndex);
           stringIndex++;

           if (atNextWord(stringIndex)) {
              stringIndex++; // skip whitespace 
              deltaTimeForEachChar = calcNextCharDeltaForNextWord();
           } else {
              elapsedTimeForNextChar += deltaTimeForEachChar;
           }
        }

        // else -- we haven't reached the next time to change char attribute yet.
        // keep polling.
     }

例如,我的SSCCE:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.LinkedList;
import java.util.List;

import javax.swing.*;
import javax.swing.text.*;

public class Reminder3 {
   private static final String TEXT = "arey chod chaad ke apnee saleem ki gali anarkali disco chalo";
   private static final String[] WORDS = TEXT.split(" ");
   private static final int[] TIMES = { 100, 400, 300, 900, 1000, 600, 200,
         700, 700, 200, 200, 200, 200 };
   private static final int POLLING_TIME = 12;

   private StyledDocument doc;
   private JTextPane textpane;
   private JPanel mainPanel = new JPanel();
   private List<ReminderWord> reminderWordList = new LinkedList<ReminderWord>();
   private Timer timer;

   // private int stringIndex = 0;

   public Reminder3() {
      doc = new DefaultStyledDocument();
      textpane = new JTextPane(doc);
      textpane.setText(TEXT);
      javax.swing.text.Style style = textpane.addStyle("Red", null);
      StyleConstants.setForeground(style, Color.RED);

      JPanel textPanePanel = new JPanel();
      textPanePanel.add(new JScrollPane(textpane));

      JButton startBtn = new JButton(new AbstractAction("Start") {

         @Override
         public void actionPerformed(ActionEvent arg0) {
            goThroughWords();
         }
      });
      JPanel btnPanel = new JPanel();
      btnPanel.add(startBtn);

      mainPanel.setLayout(new BorderLayout());
      mainPanel.add(textPanePanel, BorderLayout.CENTER);
      mainPanel.add(btnPanel, BorderLayout.SOUTH);
   }

   public void goThroughWords() {
      if (timer != null && timer.isRunning()) {
         return;
      }
      doc = new DefaultStyledDocument();
      textpane.setDocument(doc);
      textpane.setText(TEXT);

      javax.swing.text.Style style = textpane.addStyle("Red", null);
      StyleConstants.setForeground(style, Color.RED);

      int wordStartTime = 0;
      for (int i = 0; i < WORDS.length; i++) {

         if (i > 0) {
            wordStartTime += TIMES[i - 1];
         }
         int startIndexPosition = 0; // set this later
         ReminderWord reminderWord = new ReminderWord(WORDS[i], TIMES[i],
               wordStartTime, startIndexPosition);
         reminderWordList.add(reminderWord);
      }

      int findWordIndex = 0;
      for (ReminderWord word : reminderWordList) {

         findWordIndex = TEXT.indexOf(word.getWord(), findWordIndex);
         word.setStartIndexPosition(findWordIndex);
         findWordIndex += word.getWord().length();
      }

      timer = new Timer(POLLING_TIME, new TimerListener());
      timer.start();
   }

   public JComponent getMainPanel() {
      return mainPanel;
   }


   private void setNextCharAttrib(int textIndex) {
      doc.setCharacterAttributes(textIndex, 1,
            textpane.getStyle("Red"), true);      
   }

   private class TimerListener implements ActionListener {
      private ReminderWord currentWord = null;
      private long startTime = System.currentTimeMillis();

      @Override
      public void actionPerformed(ActionEvent e) {
         if (reminderWordList == null) { 
            ((Timer) e.getSource()).stop();
            return;
         }

         if (reminderWordList.isEmpty() && currentWord.atEnd()) {
            ((Timer) e.getSource()).stop();
            return;
         }

         // if just starting, or if done with current word
         if (currentWord == null || currentWord.atEnd()) {
            currentWord = reminderWordList.remove(0); // get next word
         }

         long totalElapsedTime = System.currentTimeMillis() - startTime;
         if (totalElapsedTime > (currentWord.getStartElapsedTime() + currentWord
               .getIndex() * currentWord.getTimePerChar())) {
            setNextCharAttrib(currentWord.getStartIndexPosition() + currentWord.getIndex());

            currentWord.increment();
         }

      }
   }

   private static void createAndShowGui() {
      Reminder3 reminder = new Reminder3();

      JFrame frame = new JFrame("Reminder");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(reminder.getMainPanel());
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }


   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }

}

class ReminderWord {
   private String word;
   private int totalTime;
   private int timePerChar;
   private int startTime;
   private int startIndexPosition;
   private int index = 0;

   public ReminderWord(String word, int totalTime, int startTime,
         int startIndexPosition) {
      this.word = word;
      this.totalTime = totalTime;
      this.startTime = startTime;
      timePerChar = totalTime / word.length();
      this.startIndexPosition = startIndexPosition;
   }

   public String getWord() {
      return word;
   }

   public int getTotalTime() {
      return totalTime;
   }

   public int getStartElapsedTime() {
      return startTime;
   }

   public int getTimePerChar() {
      return timePerChar;
   }

   public int getStartIndexPosition() {
      return startIndexPosition;
   }

   public int increment() {
      index++;
      return index;
   }

   public int getIndex() {
      return index;
   }

   public boolean atEnd() {
      return index > word.length();
   }

   public void setStartIndexPosition(int startIndexPosition) {
      this.startIndexPosition = startIndexPosition;
   }

   @Override
   public String toString() {
      return "ReminderWord [word=" + word + ", totalTime=" + totalTime
            + ", timePerChar=" + timePerChar + ", startTime=" + startTime
            + ", startIndexPosition=" + startIndexPosition + ", index=" + index
            + "]";
   }

}
 类似资料:
  • 问题内容: 为了准确起见,我喜欢@Tony Docherty On CR建议的逻辑和示例。这是链接。 为了突出显示给定的单词,一次又一次地总是有几微秒的延迟。如果我要突出显示一些单词:“ hello how are”,并且每个单词的值分别(延迟)为:200,300,400 ms,那么计时器实际花费的时间总是更多。说而不是200毫秒,而是216毫秒。像这样,如果我有很多话……最后,额外的延迟是显而易

  • 问题内容: 我目前正在尝试将我们的应用程序从Java 8迁移到Java 10,并在几个月后将迁移到Java 11。 我们有一个遗留系统,它使用了一些API,我猜它们将被删除或将被删除。我们正在使用ant脚本(1.10.1)进行应用程序编译。 以下是构建我们的应用程序的部分: 在编译期间,移至Java 10后,与Java Swing相关的错误很少(还是较旧的错误,但必须忍受)。由于我仍不完全了解Ja

  • 问题内容: 嗨,我正在研究一个项目(Java内存游戏),首先我想了解摆动计时器的工作原理。首先,我主班级实现了和。而且我使用计时器,如果有两张卡用户选择了不同的图片,那么我会给他几秒钟的时间来查看图片,然后它们将再次关闭。但是如果用户选择了两张不同的图片,他们会突然关闭,所以我看不到第二张图片。我阅读了一些有关swing timer的教程,但我想我对SSCCE的创建方式理解不对。如果您能帮助我,我

  • 问题内容: 我在游戏中使用了摇摆计时器,但是当游戏运行时,它似乎有平稳运行的时刻和减速的时刻。 为什么时间在波动? 我该如何解决? 这是我的代码示例。在我的实际程序中,我正在绘制图像,而不仅仅是矩形。还有很多碰撞检测和其他小的计算正在发生。 另外,这是游戏的Jar文件的链接,因此您可以运行它,(满是)明白我的意思。http://dl.dropbox.com/u/8724803/Get%20To%2

  • 我试图为下面的JSON创建一个swagger文档,但是我得到了下面的错误:带有“type: array”的模式需要一个同级“items:”字段 JSON: 有人能帮我得到这个JSON的招摇博士。 任何帮助都将不胜感激。

  • 我使用了一个教程,一切都很好,直到我开始处理swagger 2的依赖。我现在想知道是否有办法解决这个问题。 招摇配置: pom.xml: 错误: 搜索关于我试图改变版本到2.8.0,2.7.0,3.0.0...也返回错误。该应用程序是一个带有任务列表活动的apirest。