使用Timer在JTextArea中实现打字机效果?

我正在制作一个文本冒险游戏,并遇到了一个问题,我无法以我想要的方式显示我的一些文本。 当输入一些单词时,玩家可以开始引入新房间。 我希望这个介绍有“打字机”的效果。 此事件需要在我的程序ActionPerformed方法中进行。 例如,当用户键入“Move”然后按Enter键时,我希望生成的文本一次打印一个字符。

以下是我在ActionPerformed之外使用的当前方法来实现此效果:

public void slowPrint(String message, long millisPerChar) { //makes it so that the player cannot input while the text is being displayed userinput.setEditable(false); String o; for (int i = 0; i < message.length(); i++) { //adds each letter one-by-one o = "" + message.charAt(i); output.append(o); //moves the JTextArea to the bottom output.setCaretPosition (output.getDocument ().getLength ()); //delay so that you can see each letter being added try { Thread.sleep(millisPerChar);; } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //after the text is displayed allow the user to input again userinput.setEditable(true); } 

如何使用类似的方法来使用Timer类,这样它就不会阻止我的线程更新GUI。

这是我使用相关方法执行的操作:

 public void actionPerformed(ActionEvent e) { s = userinput.getText(); output.append("\n"); userinput.setText(""); for (int i = 0; i<whatRoom.length; i++) { if (whatRoom[i]) { switch(i) { case 0: controlOne(s); break; case 1: break; case 2: break; case 3: break; case 4: break; case 5: break; } } } } 

而“controlOne()”方法:

 private void controlOne(String s) { if(one.getHasLight() == true) { if(!one.getPushYes()) { output.append(one.dealWithLight(s, output)); } else output.append(one.commands(s)); } else output.append(one.commands(s)); } 

而不是一直追加我想要使用类似于我的“slowPrint”方法的东西。

希望这是有道理的,任何帮助将不胜感激。

Timer的两个基本值是

  1. 延误
  2. ActionListener

如果在slowPrint方法中创建计时器,请slowPrint保留对它的引用,以便稍后停止。 因此,您必须稍后添加ActionListener 。 在ActionListener您可以通过其他变量跟踪当前文本位置(忘记for循环)。 然后,您只需手动检查是否已打印所有文本,并相应地停止计时器。

如果您希望最初也发生延迟,只需调用timer.setInitialDelay(delay)

GIF


修改了slowPrint方法的示例:

 import java.awt.BorderLayout; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.Timer; public class Foo { private JTextField input; private JTextArea output; public static void main(String[] args) throws IOException { EventQueue.invokeLater(() -> new Foo().createAndShowGUI()); } public void createAndShowGUI() { output = new JTextArea(); output.setEditable(false); input = new JTextField(); input.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { slowPrint("This is a test answer.\n", 100); } }); JPanel contentPane = new JPanel(new BorderLayout(5, 5)); contentPane.add(input, BorderLayout.NORTH); contentPane.add(output); JFrame frame = new JFrame("Example"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setContentPane(contentPane); frame.setSize(800, 600); frame.setVisible(true); } public void slowPrint(String message, int millisPerChar) { input.setEditable(false); input.setFocusable(false); Timer timer = new Timer(millisPerChar, null); timer.addActionListener(new ActionListener() { int counter = 0; @Override public void actionPerformed(ActionEvent e) { output.append(String.valueOf(message.charAt(counter++))); output.setCaretPosition(output.getDocument().getLength()); if (counter >= message.length()) { timer.stop(); input.setEditable(true); input.setFocusable(true); input.requestFocusInWindow(); } } }); timer.start(); } }