在CR上.这是 Link.
为了强调给定的单词,一次又一次地总是有几微秒的延迟.如果我有话要突出说:“你好怎么样”,每个单词的值分别是(延迟):200,300,400 ms,那么定时器的实际时间总是更多.说,而不是200毫秒,它需要216毫秒.像这样,如果我有很多话,最后,额外的延迟是显而易见的.
我必须强调每个字母说:’h””””’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:
让我们用单词“测试”,并说需要突出显示1秒钟,因此每个字母都突出显示250ms.
以你最初的方式做事情,这意味着你为每个字母设置了一个250ms的定时器,但是如果每个周期实际上花费了260ms,并且让’e’周期花费了400ms(可能是由于GC或其他使用cpu周期的)这个词的结尾应该比你应该有180ms.这个错误将继续为每个单词构建,直到错误是如此之大,突出显示不再是视觉上同步的.
我正在尝试的方式,而不是反复说这个字母需要突出显示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),但至少我将保持广泛的同步.
EDITED SSCCE
UPDATE2
在第一阶段,我将每个单词的时间顺序.也就是说,当用户击中ESC键时,存储一个特定单词的时间(当歌曲在背景中播放时).当按下ESC键时,当前单词突出显示,并在当前时间上花费时间字存储在数组中.我继续存储时间.当用户结束时,现在我想根据设置的时间突出显示这些单词.所以在这里,用户的时机很重要.如果时间快,话语的亮点也是如此之快,反之亦然.
新更新:进度
下面的答案有不同的逻辑,但令我吃惊的是,他们的工作或多或少是相同的.我发现所有的逻辑(包括我的)的一个非常奇怪的问题是,他们似乎完美地工作了几条线,但之后他们获得速度,这也不慢,但是有很大的区别.
此外,如果您认为我应该以不同的方式思考,您的建议是非常感激的.
解决方法
使用该代码,我通过System.out.println()使用System.nanoTime()放置一些测量系统,这将帮助我们看到发生了什么:
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};//word/letters timings private String[] individualWordsToHighlight = {" \nHello\n"," world\n"," Hello"," world"," 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(); } }); } }
我的电脑上的输出是:
Started: 10289712615974
Time the char should take: 166
Time it took: 165
Difference 1
…
Time the char should take: 166
Time it took: 155
Difference 11
…
Time the char should take: 166
Time it took: 5
Difference 161
Stopped: 10299835063084
Time it should take in total: 9960
Time it took using accumulator of time taken for each letter: 5542
Difference: 4418
Time it took using difference (endTime-startTime): 10122
Difference: -162
因此,我的结论是,摆动计时器实际上运行速度比我们预期的要快,因为Timers actionPerformed中的代码不一定需要长久的字母,预计突出显示时间会导致雪崩效应,即定时器运行的速度越快/越慢/更小的差异将会变得和定时器下一次执行重新启动(..)将需要在不同的时间,即更快或更慢.
在代码中做这个:
//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为4ms):
Started: 10813491256556
Time the char should take: 166
Time it took: 164
Difference 2
…
Time the char should take: 166
Time it took: 164
Difference 2
…
Time the char should take: 166
Time it took: 162
Difference 4
Stopped: 10823452105363
Time it should take in total: 9960
Time it took using accumulator of time taken for each letter: 9806
Difference: 154
Time it took using difference (endTime-startTime): 9960
Difference: 0