MIDP Graphics.drawString() 僅支持單行繪製,RPG 對話框、任務日誌、幫助文檔等多行文本場景必須自行實現換行邏輯。本文提供一個生產級 getSubsection() 方法,完美處理中英混排、單詞完整性、顯式換行/縮進、指定像素寬度約束。
import java.util.Vector;
import javax.microedition.lcdui.Font;
/**
* 將文本按指定像素寬度分行
* @param strSource 待分行文本
* @param font 繪製字體
* @param width 最大行寬(像素)
* @param strSplit 斷詞分隔符(英文模式用 " ,.?!",中文模式傳 "")
* @return Vector 分行結果
*/
public Vector getSubsection(String strSource, Font font, int width, String strSplit) {
Vector lines = new Vector();
String temp = strSource;
int lastLen = 1; // 上次分行長度,作為下次搜索起點優化
try {
while (temp.length() > 0) {
// 1. 檢查顯式換行符 \n
int nlPos = temp.indexOf('\n');
if (nlPos > 0) {
// 若 \n 前內容未超寬,直接按 \n 分行
if (font.stringWidth(temp.substring(0, nlPos)) <= width) {
lines.addElement(temp.substring(0, nlPos));
temp = temp.substring(nlPos + 1);
lastLen = 1;
continue;
}
// 否則忽略 \n,按寬度強制分行
nlPos = -1;
}
int cutPos;
if (nlPos == -1) { // 無顯式換行或 \n 被忽略
// 2. 二分/線性搜索找到最大不超寬位置
if (lastLen > temp.length()) {
cutPos = temp.length(); // 剩餘長度小於上次,直接取全
} else {
cutPos = lastLen;
int step = font.stringWidth(temp.substring(0, cutPos)) > width ? -1 : 1;
// 尋找臨界點:[0,cutPos] 寬度 ≤ width 且 [0,cutPos+1] > width
while (cutPos < temp.length() &&
!(font.stringWidth(temp.substring(0, cutPos)) <= width &&
font.stringWidth(temp.substring(0, cutPos + 1)) > width)) {
cutPos += step;
if (cutPos == temp.length()) break;
}
}
// 3. 英文單詞不拆分邏輯
if (strSplit.length() > 0 && cutPos < temp.length()) {
int origCut = cutPos;
// 向前回溯找分隔符
while (cutPos > 0 && strSplit.indexOf(temp.charAt(cutPos - 1)) == -1) {
cutPos--;
}
// 回溯到行首仍無分隔符(單詞超長),恢復強制切分
if (cutPos == 0) cutPos = origCut;
}
} else {
cutPos = nlPos; // 使用 \n 位置
}
// 4. 記錄分行結果
lastLen = cutPos;
lines.addElement(temp.substring(0, cutPos));
// 5. 處理剩餘文本
if (cutPos == temp.length()) {
temp = "";
} else {
temp = temp.substring(cutPos);
// 跳過殘留的 \n
if (temp.length() > 0 && temp.charAt(0) == '\n') {
temp = temp.substring(1);
}
}
}
} catch (Exception e) {
System.out.println("getSubsection error: " + e);
}
return lines;
}
| 特性 | 實現細節 | 代碼位置 |
|---|---|---|
| 像素精確換行 | 使用 font.stringWidth() 實測寬度,非字符計數 | 搜索循環條件判斷 |
| 英文單詞保護 | strSplit 指定分隔符,向前回溯至最近分隔符 | 第 3 步回溯邏輯 |
| 中日韓逐字換行 | strSplit="" 禁用回溯,直接按像素切分 | 參數判斷分支 |
| 顯式換行 \n | 優先識別,若未超寬則強制換行 | 第 1 步 nlPos 處理 |
| 縮進 \t 支持 | 調用方預處理 \t → " " (6空格) 或擴展算法 | 預處理/擴展點 |
| 性能優化 | lastLen 記憶上次分行長度,作為下次搜索起點 | 變量初始化與更新 |