You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在JTextPane中实现文字逐字符延迟显示及点击跳过效果?

实现方案

核心逻辑说明

  • 用javax.swing.Timer做延迟触发器,避免阻塞Swing的事件调度线程(EDT)导致界面卡顿
  • 通过操作JTextPane内置的StyledDocument实现字符追加,替代JTextArea的append方法
  • 增加状态控制变量和Timer实例引用,支持点击事件触发直接展示全量文本、终止逐字输出

第一步:修改自定义JTextPanePlus类

在原有基础上增加逐字输出相关的方法和变量,代码如下:

import javax.swing.*;
import javax.swing.text.BadLocationException;
import javax.swing.text.StyledDocument;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class JTextPanePlus extends JTextPane {
    Image image;
    int x,y,width,height;
    // 逐字输出相关变量
    private Timer typeTimer;
    private String fullText; // 要显示的完整文本
    private int currentCharIndex = 0; // 当前已输出的字符下标
    private int typeSpeed = 50; // 逐字输出间隔,单位毫秒,可自行调整
    private boolean isTyping = false; // 是否正在逐字输出

    public JTextPanePlus(){
        super();
        initTypeListener(); // 初始化点击终止的监听器
    }

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

    // 初始化鼠标点击终止逐字输出的监听器
    private void initTypeListener() {
        this.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (isTyping) {
                    stopTypingAndShowFull();
                }
            }
        });
    }

    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 startTyping(String text) {
        // 如果上一次还在输出,先停止
        if (isTyping) {
            stopTypingAndShowFull();
        }
        this.fullText = text;
        this.currentCharIndex = 0;
        setText(""); // 清空原有内容
        isTyping = true;
        // 初始化Timer
        typeTimer = new Timer(typeSpeed, e -> {
            if (currentCharIndex < fullText.length()) {
                try {
                    StyledDocument doc = getStyledDocument();
                    doc.insertString(doc.getLength(), String.valueOf(fullText.charAt(currentCharIndex)), null);
                    currentCharIndex++;
                } catch (BadLocationException ex) {
                    ex.printStackTrace();
                }
            } else {
                // 全部输出完毕,停止Timer
                stopTyping();
            }
        });
        typeTimer.start();
    }

    // 停止逐字输出并显示完整文本
    public void stopTypingAndShowFull() {
        stopTyping();
        setText(fullText);
    }

    // 停止Timer重置状态
    private void stopTyping() {
        if (typeTimer != null && typeTimer.isRunning()) {
            typeTimer.stop();
        }
        isTyping = false;
        currentCharIndex = 0;
    }

    // 可自定义打字速度的方法
    public void setTypeSpeed(int speedMs) {
        this.typeSpeed = speedMs;
    }

    @Override
    public void paint(Graphics g){
        g.drawImage(image, x, y, width, height, null);
        super.paint(g);
    }
}

第二步:修改Frame2类的调用逻辑

把原来直接setText的逻辑替换为调用startTyping方法即可,鼠标点击触发显示的地方调整如下:

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class Frame2 extends JFrame {
    private JPanel contentPane;
    Frame1 frms = new Frame1();
    // 保留你原有icon4的定义即可,此处仅为示例
    private ImageIcon icon4 = new ImageIcon("你的背景图本地路径");

    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();
        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.setBounds(60, 343, 527, 111);
        contentPane.add(pnText1);

        // 保留你原有backgroundLabel的定义即可,此处仅为示例
        JLabel backgroundLabel = new JLabel();
        backgroundLabel.setBounds(0,0,799,526);
        contentPane.add(backgroundLabel);

        backgroundLabel.addMouseListener(new MouseAdapter(){
            @Override
            public void mouseClicked(MouseEvent arg0) {
                pnText1.setVisible(true);
                // 调用逐字输出方法,传入你要显示的完整文本
                pnText1.startTyping("Some text dasdasdasdasdasdas 可替换为你的小说/游戏文本内容");
                // 若需调整打字速度,可提前调用pnText1.setTypeSpeed(30); 数值越小速度越快
            }
        });
    }
}

效果说明

  • 点击背景后,JTextPanePlus会以设置的间隔逐字输出文本
  • 输出过程中点击JTextPanePlus区域,会立刻停止逐字输出,直接展示完整文本
  • 全程所有UI操作都在Swing的EDT线程执行,不会出现界面卡顿的问题,符合Swing线程规范

内容的提问来源于stack exchange,提问作者Overlord99

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 21:54:08