如何使文本在 JTextPane 中出现延迟?

How to make text appear delayed in JTextPane?

public class Frame2 扩展 JFrame {

private JPanel contentPane;
Frame1 frms = new Frame1();

public static void main(String[] args) {
    
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            try {
                
                Frame2 frame = new Frame2();
                frame.setVisible(true);
                frame.setResizable(false);
                
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}

public Frame2() {
    
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setBounds(250, 100, 799, 526);
    contentPane = new JPanel();
    contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
    setContentPane(contentPane);
    contentPane.setLayout(null);
    
   // ...

    final JTextPanePlus pnText1 = new JTextPanePlus(); // final because listener requires this 
    pnText1.setImage(icon4, -70, -100, 600, 500);
    pnText1.setVisible(false);
    pnText1.setFont(new Font("Tahoma", Font.PLAIN , 16));
    pnText1.setEditable(false);
    pnText1.setForeground(Color.WHITE);
    
    pnText1.setText("Some text dasdasdasdasdasdas");
    pnText1.setBounds(60, 343, 527, 111);
    contentPane.add(pnText1);

   //...

    backgroundLabel.addMouseListener(new MouseAdapter(){
                 @Override
                 public void mouseClicked(MouseEvent arg0) {

                    pnText1.setVisible(true);

                        }
                    });
    // ...

}

为 JTextPane 选择图像的方法

public class JTextPanePlus 扩展了 JTextPane {

Image image;
int x,y,width,height;
public JTextPanePlus(){
    super();
}

public JTextPanePlus(String text){
    super();
}

public void setImage(ImageIcon icon, int x, int y, int width, int height) {
    
    this.image = icon.getImage();
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    setOpaque(false);
    repaint();
}
public void paint(Graphics g){
    
    g.drawImage(image, x, y, width, height, null);
    super.paint(g);
}

}

我无法让 JTextPane (JTextPanePlus) 的文本在每个字符之间出现轻微延迟。基本上我想为小说或冒险创造一种效果。如果用户不想等待整个文本出现,我可以添加一个“mouseListener”并立即显示文本(停止该方法)。

现在的问题是我不知道该怎么做。这里的大多数主题都涵盖了 JTextArea 的内容,我不确定是否可以为 JTextPane 做同样的事情。

我试着做“for(char c : s.toCharArray())”,但后来我不知道如何处理“c”,而且这种方法似乎只适用于打印。尝试使用“Thread.sleep”,但这会冻结整个 JFrame。 Java.swing.timer 可以工作,但 JTextPane 在使用时不支持“.append”。

我 运行 别无选择,而且我不是这方面的专家。这是我第一次从事如此艰巨的工作。另外,如果这没有得到最好的解释,我真的很抱歉。

JTextPaneJTextArea 的基本思路相同。问题是,JTextPane 在渲染功能方面更强大,并且不提供 JTextArea 提供的一些辅助方法,例如 append,相反,您将拥有自己做。

话虽如此,这是一个非常基本的示例。此示例从嵌入式文件资源加载“星球大战,新希望”脚本并打印它,就像打字机一样。

如果您点击 停止,它将停止 Timer 并为当前行附加剩余的文本。有趣的是,如果你再次启动它,它会从中断的地方继续,整洁的副作用。

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.Timer;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JTextPane textPane;
        private Timer timer;
        private List<String> lines;

        private String currentLine;
        private int currentLinePosition;

        public TestPane() {
            setLayout(new BorderLayout());
            textPane = new JTextPane();
            add(new JScrollPane(textPane));

            JPanel actionsPane = new JPanel(new GridBagLayout());

            JButton makeItSo = new JButton("Make it so");
            JButton stop = new JButton("Stop");

            actionsPane.add(makeItSo);
            makeItSo.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    makeItSo();
                    makeItSo.setEnabled(false);
                    stop.setEnabled(true);
                }
            });

            actionsPane.add(stop);
            stop.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    stopTimer();

                    makeItSo.setEnabled(true);
                    stop.setEnabled(false);
                }
            });

            add(actionsPane, BorderLayout.SOUTH);

            loadScript();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }

        protected void loadScript() {
            lines = new ArrayList<>(128);
            try (BufferedReader br = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("/scripts/StarWarANewHope.txt")))) {
                for (String line = br.readLine(); line != null; line = br.readLine()) {
                    lines.add(line);
                }
            } catch (IOException ex) {
                Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        protected int startPosition(String text) {
            if (text.isEmpty() || text.isBlank()) {
                return 0;
            }
            int index = 0;
            while (Character.isWhitespace(text.charAt(index))) {
                index++;
            }
            return index;
        }

        protected void insertWithOutError(String text) {
            try {
                insert(text);
            } catch (BadLocationException ex) {
                Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        protected void insert(String text) throws BadLocationException {
            Document document = textPane.getDocument();
            document.insertString(document.getLength(), text, null);
        }

        protected void stopTimer() {
            if (timer != null) {
                timer.stop();
                timer = null;
            }

            if (currentLine != null) {
                if (currentLinePosition < currentLine.length()) {
                    String text = currentLine.substring(currentLinePosition);
                    try {
                        insert(text);
                        insertWithOutError("\n");
                    } catch (BadLocationException ex) {
                        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
                    }        
                }
            }

            currentLine = null;
        }

        protected void makeItSo() {
            stopTimer();
            if (lines.isEmpty()) {
                return;
            }
            try {
                // Pop the first line
                currentLine = lines.remove(0);
                int offset = startPosition(currentLine);
                if (currentLine.isBlank() || offset == currentLine.length()) {
                    insertWithOutError("\n");
                } else {
                    String leading = currentLine.substring(0, offset);
                    insert(leading);
                }
                currentLinePosition = 0;
                currentLine = currentLine.trim();

                timer = new Timer(5, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if (currentLinePosition < currentLine.length()) {
                            try {
                                String next = currentLine.substring(currentLinePosition, currentLinePosition + 1);
                                insert(next);
                                currentLinePosition++;
                            } catch (BadLocationException ex) {
                                Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
                                stopTimer();
                            }
                        } else {
                            insertWithOutError("\n");
                            makeItSo();
                        }
                    }
                });
                timer.start();
            } catch (BadLocationException ex) {
                Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

    }
}

现在,JTextPane 可以支持 StyledDocuments,这使它变得有些复杂。你会注意到我什至没有尝试支持“属性”,你必须自己解决这个问题