当前位置: 首页 > 面试题库 >

问题:创建一个非常准确的Swing计时器

祖波光
2023-03-14
问题内容

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

为了突出显示给定的单词,一次又一次地总是有几微秒的延迟。如果我要突出显示一些单词:“ hello how
are”,并且每个单词的值分别(延迟)为:200,300,400
ms,那么计时器实际花费的时间总是更多。说而不是200毫秒,而是216毫秒。像这样,如果我有很多话……最后,额外的延迟是显而易见的。

我必须突出显示每个字母:’h’e’l’l‘0’每个字母应获得200 /长度(即5)= 40毫秒左右。设置每个字母后的延迟时间。

我的逻辑是,以当前时间为准startTime,在开始该过程之前。另外,计算totalDelay为totalDelay + = delay /
.length()。

现在检查条件:(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给出的EG:

让我们以单词“
Test”为例,说它需要突出显示1秒钟,因此每个字母都突出显示250ms。以您原来的方式做事,确实意味着您为每个字母设置了250ms的计时器,但是如果每个周期实际上花费了260ms,并且可以说“
e”周期花费了400ms(可能是由于GC或其他使用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键时,当前单词会突出显示,并且当前单词所花费的时间单词存储在数组中。我继续存储时间。当用户结束时,现在我想按照设置的时间突出显示单词。因此,在这里,用户的时间安排很重要。如果时间安排得很快,那么单词的突出显示也可以,反之亦然。

新更新:进度

以下答案具有不同的逻辑,但令我惊讶的是,它们的工作原理大致相同。我在所有逻辑(包括我的逻辑)中发现的一个非常非常奇怪的问题是,它们似乎只适用于几行,但是在获得速度之后,这也不算慢,但有很大的不同。

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


问题答案:

好的,所以我一直在看一些代码(我在上一个关于卡拉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
Timer实际上运行的速度比我们预期的要快,因为Timers中的代码actionPerformed不一定要花预期的字母突出显示时间的时间,这当然会引起雪崩效应,即,计时器运行的越快/越慢,则运行时间越大/差异将变小,下次执行的计时器restart(..)将在不同的时间进行,即更快或更慢。

在代码中执行以下操作:

//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();

产生更准确的结果(Ive的最大延迟是每个字母快4毫秒):

开始:10813491256556

字符应花费的时间:166

花费时间:164

差异2

字符应花费的时间:166

花费时间:164

差异2

字符应花费的时间:166

花费时间:162

差异4

停止:10823452105363

总耗时:9960

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

差异:154

使用差异所花费的时间(endTime-startTime):9960

差异:0



 类似资料:
  • 为了使准确,我喜欢@Tony Docherty On CR建议的逻辑和示例。这是链接。 为了一次又一次地突出显示给定的单词,总是会有几微秒的延迟。如果我有要突出显示的单词,比如说:“你好,你好”,每个单词的值分别是(延迟):200300400毫秒,那么计时器实际花费的时间总是更多。比如说,如果我有很多单词的话,需要216毫秒,而不是200毫秒。。最后,额外的延迟是显而易见的。 我必须突出显示每个字

  • 问题内容: 我需要创建一个简单但准确的计时器。 这是我的代码: 恰好在3600秒后,它将打印约3500秒。 为什么不准确? 如何创建准确的计时器? 问题答案: 为什么不准确? 因为您正在使用setTimeout()或setInterval()。他们不能被信任,没有针对他们的准确性保证。它们被允许 任意滞后,并且它们不能保持恒定的步伐而是趋向于漂移(如您所观察到的)。 如何创建准确的计时器? 使用该

  • 问题内容: 对于这个问题,我很抱歉,但是我已经阅读了很多东西,而且看来我还不懂如何做一个计时器。所以我发布我的代码: 我正在尝试使该对象每200毫秒朝目标移动一次。我没有自我尝试过,它给了我同样的错误: 我不知道如何将计时器连接到带有参数的函数。我以为我没有正确使用SLOT参数,但这给了我这些错误。我想这都是错的。我将不胜感激:) 问题答案: 使用新样式的信号,它们更容易理解。 交换- 与- 一个

  • 我在我的查询中添加了一个投影,它创建了一个像。。。 参见链接 和相同的错误y8_,y5_意味着所有关闭它给了一个错误。 我把它改成了。。。 它起作用了。但是我不知道如何在HQL修改它?

  • 问题内容: 我创建了一个计时器,该计时器在按下按钮时启动,上面是运行的代码。谁能帮我创建一个计数为30的计时器?现在,当我运行它时,在标签中设置文本“ 30”,但我希望它从0开始并一直计数到30。 问题答案: 每次您的计时器运行时,它都会执行从0到30的循环,因此仅在循环结束时才刷新UI。您需要将i保留为成员,并在每次这样调用该方法时对其进行更新: 当然,一旦达到i = 30,您就应该取消时间,否

  • timeLabel应该从60倒数到0,但我还没有实现一个持续时间。例如这样它就像一个真正的倒数计时器。我该怎么做。另一个问题是运行此代码时游戏不会在模拟器中启动。我得到一个错误,我被重定向到AppDelegate.swift文件: