在遊戲開發、多媒體展示等場景中,「逐字打印」效果能顯著提升用戶體驗。本文基於 Loon Framework 實現一個可復用的打字機動畫組件,支持多行文本、自定義速度、背景圖片等特性。
繼承 Panel 並實現 Runnable,採用雙緩衝繪圖技術消除閃爍。
private static final int _WIDTH = 500;
private static final int _HEIGHT = 375;
private int _MAX_COUNT = 10; // 最大對話數
private char _flag = '|'; // 換行分隔符
private Image _screen; // 雙緩衝圖像
private Graphics _bg; // 後台繪圖上下文
private Thread _L_WriterType; // 動畫線程
private String[] _M_Text; // 原始文本數組
private String[] _C_Text; // 緩存顯示文本
private int _L_Speed; // 打字速度(ms)
private int _L_Delay; // 行間延遲(ms)
public WriterType(Image img, String[] strContent, int speed, int delay) {
init(img, strContent, speed, delay);
}
private void init(Image img, String[] strContent, int speed, int delay) {
// 背景與字體設置
setBackground(Color.BLACK);
_back = img;
_L_TTF = new Font("宋體", 0, 15);
_L_fClr = new Color(250, 250, 250);
// 文本數組拷貝
System.arraycopy(strContent, 0, _M_Text, 0, strContent.length);
_N_Text = strContent.length - 1;
// 雙緩衝初始化
_screen = new BufferedImage(_WIDTH, _HEIGHT, BufferedImage.TYPE_INT_ARGB);
_bg = _screen.getGraphics();
// 啟動動畫線程
_isRun = true;
_L_WriterType = new Thread(this);
_L_WriterType.start();
}
public void run() {
while (_isRun) {
try {
_S_COUNT++; // 字符累加計數
// 當前行文本完成
if (_S_COUNT == _M_Text[_T_COUNT].length()) {
_C_Text[_L_COUNT] = _M_Text[_T_COUNT].substring(_F_COUNT, _S_COUNT);
begin(); // 重置狀態準備下一行
Thread.sleep(_L_Delay);
repaint();
}
// 判斷是否為分隔符
boolean shortSleep = false;
if (_M_Text[_T_COUNT].charAt(_S_COUNT) == _flag) {
_L_COUNT++;
_S_COUNT++;
_F_COUNT = _S_COUNT;
shortSleep = true; // 分隔符處延長停頓
}
// 更新緩存文本(帶游標效果)
_C_Text[_L_COUNT] = _M_Text[_T_COUNT].substring(_F_COUNT, _S_COUNT)
+ (shortSleep ? "" : "_");
repaint();
Thread.sleep(shortSleep ? 10 * _L_Speed : _L_Speed);
} catch (InterruptedException e) {
stop();
}
}
}
public void paint(Graphics g) {
// 清空背景
_bg.setColor(_L_bClr);
_bg.fillRect(0, 0, _WIDTH, _HEIGHT);
// 繪製背景圖
_bg.drawImage(_back, 0, 0, this);
// 逐行繪製文本
_bg.setColor(_L_fClr);
_bg.setFont(_L_TTF);
for (int i = 0; i <= _L_COUNT; i++) {
if (_C_Text[i] != null) {
int top = (int) (_FontSize * (3.0 + 2.0 * i));
_bg.drawString(_C_Text[i], 50, top);
}
}
// 雙緩衝輸出
g.drawImage(_screen, 0, 0, this);
}
public static void main(String[] args) {
String[] dialogues = new String[] {
"身為劍體,|鋼鐵之血灼璃之心.|手創千劍,身經百戰;|無一敗績,無一知己.|",
"I am the bone of my sword;|Steel is my body, and fire is my blood.|"
};
Frame frm = new Frame();
frm.setTitle("Java 逐字打印動畫演示");
frm.setSize(_WIDTH, _HEIGHT + 20);
frm.setResizable(false);
// 載入背景圖(需替換為實際路徑)
Image bgImg = ImageHelper.loadImage("background.jpg", true);
frm.add(new WriterType(bgImg, dialogues, 50, 5000));
frm.setVisible(true);
}
BufferedImage 作為後台緩衝區,消除繪圖閃爍_ 字符模擬打字游標| 實現多行文本的分段顯示與停頓_isRun 標志位配合 interrupt() 實現優雅關閉ImageHelper 可替換為 ImageIO 或 ImageIconJPanel 替代 Panel)