10

正確にするためSwingTimerに、@Tony Docherty On CRが提案したロジックと例が好きです。ここにリンクがあります。

指定された単語を何度も強調表示するために、常に数マイクロ秒の遅延があります。ハイライトする単語が「こんにちは、お元気ですか」で、各単語の値が (遅延) である場合、それぞれ 200、300、400 ミリ秒の場合、タイマーにかかる実際の時間は常にそれ以上になります。200 ミリ秒ではなく、216 ミリ秒かかります。このように、言葉が多いと..結局余計な遅れが目立ちます。

各文字を強調表示する必要があります: 'h''e''l''l''0' はそれぞれ 200/length(つまり 5) = 40 ms になるはずです。各文字の後に遅延を設定します。

私の論理は、startTimeプロセスを開始する直前に、現在の時刻を言うことです。また、totalDelaytotalDelay+=delay/.length() を計算します。

ここで条件を確認してください: ( startTime+totalDelay-System.currentTime) これが -ve の場合は、時間がかかることを意味するため、文字をスキップします。正の遅延が発生するまでチェックします。これは、今までのタイミングを追加していることを意味し、プロセスが開始されたときにプロセスにかかった時間の差でオーバーチェックします。

これにより、文字のハイライトがスキップされる場合があります。

しかし、何かが間違っています。なに、わかりにくい。たぶんループに問題があります。私はそれがループに入っているのを見ました(時間が -ve かどうかを確認するために)2回だけです。しかし、そうであってはなりません。また、次の遅延の設定についてもわかりません。何か案は?

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 秒間強調表示する必要があるとします。したがって、各文字は 250 ミリ秒強調表示されます。元々の方法で物事を行うということは、文字ごとに250ミリ秒のタイマーを設定することを意味していましたが、各サイクルが実際に260ミリ秒かかり、「e」サイクルが400ミリ秒かかったとしましょう(おそらく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

Update2

最初のフェーズでは、各単語のタイミングを取ります。つまり、ユーザーが ESC キーを押すと、特定の単語の時間が保存されます (曲がバックグラウンドで再生されるときに保存されます)。ESC キーを押すと、現在の単語が強調表示され、現在の単語に費やされた時間が表示されます。 word は配列に格納されます。私はタイミングを保存し続けます。ユーザーが終了したら、設定されたタイミングに従って単語を強調表示したいと思います。ここで、ユーザーによるタイミングが重要になります。タイミングが速ければ単語の強調表示も速くなり、遅ければその逆になります。

新しい更新: 進行状況

以下の回答は論理が異なりますが、驚いたことに、それらはほぼ同じように機能します。私がすべてのロジック(私のものを含む)で見つけた非常に奇妙な問題は、それらが数行で完全に機能するように見えることですが、その後速度が向上します。これも遅くはありませんが、大きな違いがあります。

また、私が別の方法で考える必要があると思われる場合は、あなたの提案を高く評価します.

4

4 に答える 4

7

このようなことを行うには、必要な時間の粒度を許可するのに十分な速さである限り、15ミリ秒などの一定の速度で刻むスイングタイマーが必要であり、タイマー内で目的の動作をトリップする必要があると思います。経過時間はあなたが必要とするものです。

  • つまり、タイマーの遅延をまったく変更せず、必要に応じて必要な経過時間を変更するだけです。
  • while (true)EDTにループを作成しないでください。「while ループ」を Swing Timer 自体にします。
  • ロジックをより確実なものにするために、経過時間が >= 必要な時間であるかどうかを確認する必要があります。
  • 繰り返しますが、タイマーの遅延を設定しないでください。つまり、タイマーとしてではなく、ポーラーとして使用してください。xx ミリ秒ごとに常に経過時間をポーリングし、経過時間が必要以上の場合は反応します。

私が提案しているコードは次のようになります。

     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
            + "]";
   }

}
于 2013-02-23T13:39:42.070 に答える
5

わかりましたので、いくつかのコードを見てきました(カラオケタイマーに関する最後の質問で投稿したコード)

そのコードを使用して、System.nanoTime()viaSystem.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();
            }
        });
    }
}

私のPCでの出力は次のとおりです。

開始: 10289712615974

文字にかかる時間: 166

かかった時間: 165

相違点 1

...

文字にかかる時間: 166

かかった時間: 155

違い 11

...

文字にかかる時間: 166

かかった時間: 5

違い 161

停止: 10299835063084

合計所要時間: 9960

各文字にかかった時間のアキュムレータを使用してかかった時間: 5542

差: 4418

かかった時間差(endTime-startTime):10122

差: -162

したがって、私の結論は、スイングタイマーは実際には予想よりも速く実行されているというTimerこと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

于 2013-02-27T17:30:27.810 に答える
4

java.util.Timer と scheduleAtFixedRate を検討しましたか? EDT で作業を行うには少し余分な作業が必要になりますが、累積遅延の問題は修正されるはずです。

于 2013-02-23T15:17:06.590 に答える
4

ScheduledExecutorServiceSwing の Timer よりも正確になる傾向があり、複数のスレッドを実行できるという利点があります。特に、1 つのタスクが遅れても、次のタスクの開始時間には (ある程度) 影響しません。

明らかに、EDTでタスクに時間がかかりすぎる場合、これが制限要因になります。

あなたのものに基づいて提案された SSCCE を以下に示します。また、メソッドを少しリファクタリングしstartColoring、いくつかのメソッドに分割しました。また、操作のタイミングに関するフィードバックを得るために、いくつかの「ログ」を追加しました。shutdown完了したらエグゼキュータを忘れないでください。そうしないと、プログラムの終了が妨げられる可能性があります。

各単語はわずかな遅延 (私のマシンでは 5 ~ 20 ミリ秒) で色付けを開始しますが、遅延は累積されません。スケジューリングのオーバーヘッドを実際に測定し、それに応じて調整することができます。

public class Reminder {

    private static final String TEXT = "arey chod chaad ke apnee saleem ki gali anarkali disco chalo\n" +
            "arey chod chaad ke apnee saleem ki gali anarkali disco chalo\n" +
            "arey chod chaad ke apnee saleem ki gali anarkali disco chalo\n" +
            "arey chod chaad ke apnee saleem ki gali anarkali disco chalo\n" +
            "arey chod chaad ke apnee saleem ki gali anarkali disco chalo\n" +
            "arey chod chaad ke apnee saleem ki gali anarkali disco chalo";
    private static final String[] WORDS = TEXT.split("\\s+");
    private JFrame frame;
    private StyledDocument doc;
    private JTextPane textpane;
    private static final int[] TIMES = {100, 400, 300, 900, 1000, 600, 200, 700, 700, 200, 200, 
                                        100, 400, 300, 900, 1000, 600, 200, 700, 700, 200, 200,
                                        100, 400, 300, 900, 1000, 600, 200, 700, 700, 200, 200,
                                        100, 400, 300, 900, 1000, 600, 200, 700, 700, 200, 200,
                                        100, 400, 300, 900, 1000, 600, 200, 700, 700, 200, 200,
                                        100, 400, 300, 900, 1000, 600, 200, 700, 700, 200, 200, 200};
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
    private int currentLetterIndex;
    private long start; //for logging

    public void startColoring() {
        start = System.currentTimeMillis(); //for logging
        int startTime = TIMES[0];
        for (int i = 0; i < WORDS.length; i++) {
            scheduler.schedule(colorWord(i, TIMES[i + 1]), startTime, TimeUnit.MILLISECONDS);
            startTime += TIMES[i+1];
        }
        scheduler.schedule(new Runnable() {

            @Override
            public void run() {
                scheduler.shutdownNow();
            }
        }, startTime, TimeUnit.MILLISECONDS);
    }

    //Color the given word, one letter at a time, for the given duration
    private Runnable colorWord(final int wordIndex, final int duration) {
        final int durationPerLetter = duration / WORDS[wordIndex].length();
        final int wordStartIndex = currentLetterIndex;
        currentLetterIndex += WORDS[wordIndex].length() + 1;
        return new Runnable() {
            @Override
            public void run() {
                System.out.println((System.currentTimeMillis() - start) + " ms - Word: " + WORDS[wordIndex] + "  - duration = " + duration + "ms");
                for (int i = 0; i < WORDS[wordIndex].length(); i++) {
                    scheduler.schedule(colorLetter(wordStartIndex + i), i * durationPerLetter, TimeUnit.MILLISECONDS);
                }
            }
        };
    }

    //Color the letter on the EDT
    private Runnable colorLetter(final int letterIndex) {
        return new Runnable() {
            @Override
            public void run() {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("\t" + (System.currentTimeMillis() - start) + " ms - letter: " + TEXT.charAt(letterIndex));
                        doc.setCharacterAttributes(letterIndex, 1, textpane.getStyle("Red"), true);
                    }
                });
            }
        };
    }

    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.invokeLater(new Runnable() {
            @Override
            public void run() {
                Reminder reminder = new Reminder();
                reminder.initUI();
                reminder.startColoring();
            }
        });
    }
}
于 2013-02-25T17:39:45.970 に答える